【问题标题】:Waiting for page to load - UWP等待页面加载 - UWP
【发布时间】:2016-11-16 20:12:58
【问题描述】:

所以我有一个带有 XAML 页面的 Windows 10 应用程序,该页面具有 Pivot 项,Pivot 的默认行为是,每当您导航到带有 Pivot 的页面时,@ 上的第一项987654324@ 立即被选中。我想要做的是在Pivot 运行的第一项之前执行一些代码。

上下文中的代码

public sealed partial class ContentFrame : Page
{
    private IMobileServiceTable<News> NewsItems = App.MobileService.GetTable<News>();

    private List<News> AllNews;

    private List<News> Windows = new List<News>();
    private List<News> Apple = new List<News>();
    private List<News> Google = new List<News>();
    private List<News> Other = new List<News>();
    private List<News> Top = new List<News>();

    private string WeekID;

    public ContentFrame()
    {
        this.InitializeComponent();
    }

    protected override async void OnNavigatedTo(NavigationEventArgs e)
    {
        WeekID = e.Parameter as string;
        AllNews = await NewsItems.Where(News => News.Week == WeekID).ToListAsync();
        separateContent();
    }

    private void separateContent()
    {
        foreach (News item in AllNews)
        {
            Debug.WriteLine(item.Tag);
            if (item.Tag == "Windows")
                Windows.Add(item);
            else if (item.Tag == "Apple")
                Apple.Add(item);
            else if (item.Tag == "Google")
                Google.Add(item);
            else if (item.Tag == "Other")
                Other.Add(item);
            else
                Top.Add(item);
        }
    }

    private void Tags_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        IntroHead.FontWeight = FontWeights.Normal;
        WindowsHead.FontWeight = FontWeights.Normal;
        AppleHead.FontWeight = FontWeights.Normal;
        GoogleHead.FontWeight = FontWeights.Normal;
        OtherHead.FontWeight = FontWeights.Normal;

        switch (Tags.SelectedIndex)
        {
            case 0:
                IntroHead.FontWeight = FontWeights.SemiBold;
                StoryContent.Navigate(typeof(TopNewsPage), Top);
                break;
            case 1:
                WindowsHead.FontWeight = FontWeights.SemiBold;
                StoryContent.Navigate(typeof(OtherNews), Windows);
                break;
            case 2:
                AppleHead.FontWeight = FontWeights.SemiBold;
                StoryContent.Navigate(typeof(OtherNews), Apple);
                break;
            case 3:
                GoogleHead.FontWeight = FontWeights.SemiBold;
                StoryContent.Navigate(typeof(OtherNews), Google);
                break;
            case 4:
                OtherHead.FontWeight = FontWeights.SemiBold;
                StoryContent.Navigate(typeof(OtherNews), Other);
                break;
        }
    }
}

与问题相关的代码行:

StoryContent.Navigate(typeof(TopNewsPage), Top);

这执行得太快了,我想等到separateContent(); 方法完成之前我允许应用在我的 XAML 布局上导航 Pivot 控件。如果我不这样做,传入的参数 (Top) 将是 null/空,一旦我导航到 TopNewsPage,我的应用程序就会抛出异常。

我该怎么做?

我知道我制作我的应用程序的方式并不是最好的(MVVM 是我希望在未来探索的东西)但这是我的第一个应用程序,我真的只是想让它工作。我对Task 进行了一些探索,但似乎没有任何帮助,因为Pivot 导航太快了。

【问题讨论】:

  • 为什么不在InitializeComponent()之后调用separateContent();
  • 好问题。在调用separateContent(); 之前,我需要在OnNavigatedTo 方法的参数中传递WeekID,所以它不适用于我的情况。

标签: c# .net uwp uwp-xaml


【解决方案1】:

在您提到的 cmets 中,您主要希望 WeekID 先有一个值,然后再希望 Tags_SelectionChanged 工作,并且在 OnNavigatedTo 事件上设置 WeekID。我建议在 Tags_SelectionCHanged 事件上什么也不做,只要 WeekID 尚未设置。

试试:

private void Tags_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if(string.IsNullOrEmpty(WeekID))
        return;

    IntroHead.FontWeight = FontWeights.Normal;
    WindowsHead.FontWeight = FontWeights.Normal;
    AppleHead.FontWeight = FontWeights.Normal;
    GoogleHead.FontWeight = FontWeights.Normal;
    OtherHead.FontWeight = FontWeights.Normal;

    switch (Tags.SelectedIndex)
    {
        case 0:
            IntroHead.FontWeight = FontWeights.SemiBold;
            StoryContent.Navigate(typeof(TopNewsPage), Top);
            break;
        case 1:
            WindowsHead.FontWeight = FontWeights.SemiBold;
            StoryContent.Navigate(typeof(OtherNews), Windows);
            break;
        case 2:
            AppleHead.FontWeight = FontWeights.SemiBold;
            StoryContent.Navigate(typeof(OtherNews), Apple);
            break;
        case 3:
            GoogleHead.FontWeight = FontWeights.SemiBold;
            StoryContent.Navigate(typeof(OtherNews), Google);
            break;
        case 4:
            OtherHead.FontWeight = FontWeights.SemiBold;
            StoryContent.Navigate(typeof(OtherNews), Other);
            break;
    }
}

如果这对您有用,您可能希望在导航到另一个页面或页面关闭之前将事件中的 WeekID 设置为 null 或空,因为我不知道 ContentFrame 是否会保留该值在此类事件中为WeekID。只是一个提示。

【讨论】:

  • 感谢您的帮助,但这无助于解决我的问题。首先,我实际上尝试了if(Top.Count == 0) return;,而不是验证WeekID,但最终它所做的是,是的,避免了异常,但它不会加载页面。我必须将 Pivot 项目切换到其他项目,然后重新正确加载 TopNews.xaml。我需要一些停止并等待默认导航直到Top.Count != 0 的东西。您的解决方案不会等待,它需要用户重新加载 Pivot 项目。
【解决方案2】:

注意:我不认为这是解决我的问题的好方法,但它确实有效。

这是我所做的:

  1. Tags_SelectionChanged 中添加了对Top.Count 的检查,这样如果List 为空,则不会运行任何内容。
  2. separateContent(); 的末尾添加了两行代码,以便在Top 中有项目时执行第一个Pivot 项目。

如果你有更好的方法,请分享!

    private void separateContent()
    {
        foreach (News item in AllNews)
        {
            Debug.WriteLine(item.Tag);
            if (item.Tag == "Windows")
                Windows.Add(item);
            else if (item.Tag == "Apple")
                Apple.Add(item);
            else if (item.Tag == "Google")
                Google.Add(item);
            else if (item.Tag == "Other")
                Other.Add(item);
            else
                Top.Add(item);
        }
        IntroHead.FontWeight = FontWeights.SemiBold;
        StoryContent.Navigate(typeof(TopNewsPage), Top);
    }

    private void Tags_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (Top.Count < 6)
            return;
        IntroHead.FontWeight = FontWeights.Normal;
        WindowsHead.FontWeight = FontWeights.Normal;
        AppleHead.FontWeight = FontWeights.Normal;
        GoogleHead.FontWeight = FontWeights.Normal;
        OtherHead.FontWeight = FontWeights.Normal;

        switch (Tags.SelectedIndex)
        {
            case 0:
                IntroHead.FontWeight = FontWeights.SemiBold;
                StoryContent.Navigate(typeof(TopNewsPage), Top);
                break;
            case 1:
                WindowsHead.FontWeight = FontWeights.SemiBold;
                StoryContent.Navigate(typeof(OtherNews), Windows);
                break;
            case 2:
                AppleHead.FontWeight = FontWeights.SemiBold;
                StoryContent.Navigate(typeof(OtherNews), Apple);
                break;
            case 3:
                GoogleHead.FontWeight = FontWeights.SemiBold;
                StoryContent.Navigate(typeof(OtherNews), Google);
                break;
            case 4:
                OtherHead.FontWeight = FontWeights.SemiBold;
                StoryContent.Navigate(typeof(OtherNews), Other);
                break;
        }
    }

【讨论】:

    【解决方案3】:

    TLDR:https://github.com/baskren/P42.Uno.AsyncNavigation/tree/main

    需要考虑的事项:如果您可以预加载页面,那么您可以异步/等待,直到页面预加载完成后再显示。但是等等,你说,UWP Navigate 不允许预加载页面 - 而且它不是异步/等待!

    没错,但有办法解决这个问题。首先,考虑Page 继承自FrameworkElement。这意味着Page 可以是Grid 的子级或ContentPresenter 的内容。下面是一个快速的实验来说明这一点:

    ChildPage.xaml(我要在呈现前预加载的页面):

    <Page
        x:Class="NestedPagesExperiment.Shared.ChildPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:local="using:NestedPagesExperiment.Shared"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
        mc:Ignorable="d">
    
        <Page.Content>
            <Grid>
                <Grid.Children>
                    <TextBlock Text="I AM THE CHILD PAGE CONTENT!!!!" />
                </Grid.Children>
            </Grid>
        </Page.Content>
    </Page>
    

    MainPage.xaml(托管页面 - 将添加预加载的ChildPage):

    <Page
        x:Class="NestedPagesExperiment.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:local="using:NestedPagesExperiment"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
        <Page.Content>
    
            <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
                <Grid.RowDefinitions>
                    <RowDefinition Height="auto" />
                    <RowDefinition Height="40" />
                    <RowDefinition Height="*" />
                </Grid.RowDefinitions>
                <Grid.Children>
                    <TextBlock
                        Margin="20"
                        FontSize="30"
                        Text="Hello, world!" />
                    <Button
                        x:Name="_button"
                        Grid.Row="1"
                        Click="OnButtonClick"
                        Content="Add Content" />
                    <ContentPresenter x:Name="_contentPresenter" Grid.Row="2" />
                </Grid.Children>
            </Grid>
        </Page.Content>
    </Page>
    

    MainPage.xaml.cs(后面的代码):

    using NestedPagesExperiment.Shared;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Runtime.InteropServices.WindowsRuntime;
    using Windows.Foundation;
    using Windows.Foundation.Collections;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Controls.Primitives;
    using Windows.UI.Xaml.Data;
    using Windows.UI.Xaml.Input;
    using Windows.UI.Xaml.Media;
    using Windows.UI.Xaml.Navigation;
    
    // The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
    
    namespace NestedPagesExperiment
    {
        /// <summary>
        /// An empty page that can be used on its own or navigated to within a Frame.
        /// </summary>
        public sealed partial class MainPage : Page
        {
            public MainPage()
            {
                this.InitializeComponent();
            }
    
            private void OnButtonClick(object sender, RoutedEventArgs e)
            {
                var content = new ChildPage();
                _contentPresenter.Content = content;
            }
        }
    }
    

    但是等等,你说,我仍然想要页面转换!看看 Xamarin.Forms 是如何做到这一点的:

    首先,Xamarin 有一个页面容器 (FormsEmbeddedPageWrapper),它承载动态生成的页面。 FormsEmbeddedPageWrapper.xaml

    <Page
        x:Class="Xamarin.Forms.Platform.UWP.FormsEmbeddedPageWrapper"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:Xamarin.Forms.Platform.UAP"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <ContentPresenter Name="Root" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
    
        </ContentPresenter>
    </Page>
    

    注意以下几点:

    • 它的重量非常轻,因此使用传统的 UWP 页面导航可以快速加载任何内容。
    • 第 10 行有一个 ContentPresenter。正如我们将看到的,这是放置预加载内容的位置。

    同样重要的是,看看后面的代码:

        public sealed partial class FormsEmbeddedPageWrapper : Windows.UI.Xaml.Controls.Page
        {
            internal static Dictionary<Guid, ContentPage> Pages = new Dictionary<Guid, ContentPage>();
    
            public FormsEmbeddedPageWrapper()
            {
                InitializeComponent();
            }
    
            protected override void OnNavigatedTo(Windows.UI.Xaml.Navigation.NavigationEventArgs e)
            {
                base.OnNavigatedTo(e);
    
                if (e.Parameter == null)
                {
                    throw new InvalidOperationException($"Cannot navigate to {nameof(FormsEmbeddedPageWrapper)} without "
                        + $"providing a {nameof(Xamarin.Forms.Page)} identifier.");
                }
    
                // Find the page instance in the dictionary and then discard it so we don't prevent it from being collected
                var key = (Guid)e.Parameter;
                var page = Pages[key];
                Pages.Remove(key);
    
                // Convert that page into a FrameWorkElement we can display in the ContentPresenter
                FrameworkElement frameworkElement = page.CreateFrameworkElement();
    
                if (frameworkElement == null)
                {
                    throw new InvalidOperationException($"Could not find or create a renderer for the Page {page}");
                }
    
                Root.Content = frameworkElement;
            }
        }
    

    而且,为了完成这个难题,我们还需要一个扩展方法:

            internal static bool Navigate(this Windows.UI.Xaml.Controls.Frame frame, ContentPage page, Windows.UI.Xaml.Media.Animation.NavigationTransitionInfo infoOverride)
            {
    
                if (page == null)
                {
                    throw new ArgumentNullException(nameof(page));
                }
    
                Guid id = Guid.NewGuid();
    
                FormsEmbeddedPageWrapper.Pages.Add(id, page);
                if (infoOverride != null)
                    return frame.Navigate(typeof(FormsEmbeddedPageWrapper), id, infoOverride);
    
                return frame.Navigate(typeof(FormsEmbeddedPageWrapper), id);
            }
    
    

    因此,当需要导航到新页面时,Xamarin.Forms 正在执行以下操作:

    1. 他们的 Navigate 扩展功能获取预先生成的页面并将其放入静态字典 (FormsEmbeddedPageWrapper.Pages),并键入新生成的 GUID。
    2. 使用非常轻量级的 FormsEmbeddedPageWrapper 类型和上面生成的 GUID 键作为参数调用 UWP frame.Navigate(Type, object) 方法。
    3. UWP 实例化并显示FormsEmbeddedPageWrapper 的新实例。
    4. UWP 调用在 FormsEmbeddedPageWrapper 实例上的 OnNavigatedTo - 并在参数中包含 GUID 键。
    5. FormsEmbeddedPageWrapper 实例使用 GUID 键从 Pages 静态字典中提取预加载的页面内容。
    6. FormsEmbeddedPageWrapper 将其ContentPresenterContent 设置为从Pages 静态字典中检索到的预加载页面内容。

    这种方法保留了 UWP 页面过渡动画的使用,并允许预加载页面内容。

    虽然 Xamarin.Forms 代码 sn-ps 用于解释这种方法,但这种方法中没有什么不能用于普通 UWP 应用程序。

    【讨论】:

      猜你喜欢
      • 2017-09-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-20
      • 2020-01-04
      • 2017-03-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多