等待页面加载 - UWP



所以我有一个 Windows 10 应用程序,其中包含一个具有Pivot项的 XAML 页面,Pivot的默认行为是,每当导航到带有Pivot的页面时,都会立即选择Pivot上的第一项。我想做的是在运行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 ) 将为空/空,我的应用程序将在我导航到 TopNewsPage 后立即抛出异常。

我该怎么做?

我知道我制作应用程序的方式不是最好的(MVVM 是我将来想要探索的东西),但这是我的第一个应用程序,我真的只是想让它工作。我已经用Task探索了一些,但似乎没有什么帮助我的事业,因为Pivot导航太快了。

在评论中,您提到您主要希望WeekID先有一个值,然后再希望Tags_SelectionChanged工作,并且在OnNavigatedTo事件上设置WeekID。我建议只要尚未设置WeekID就不要对Tags_SelectionCHanged事件执行任何操作。

尝试:

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的值。只是一个提示。

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

这是我所做的:

  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;
        }
    }

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

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

没错,但有一种方法可以解决这个问题。 首先,考虑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. 他们的导航扩展函数采用预先生成的页面并将其放入静态字典 (FormsEmbeddedPageWrapper.Pages ),键控到新生成的 GUID。
  2. UWP frame.Navigate(Type, object) 方法使用非常轻量级的 FormsEmbeddedPageWrapper 类型和上面生成的 GUID 密钥作为参数进行调用。
  3. UWP 实例化并显示FormsEmbeddedPageWrapper的新实例。
  4. UWP 调用OnNavigatedTo FormsEmbeddedPageWrapper实例 - 并在参数中包含 GUID 密钥。
  5. FormsEmbeddedPageWrapper实例使用 GUID 键从Pages静态字典中提取预加载的页面内容。
  6. FormsEmbeddedPageWrapper将其ContentPresenterContent设置为从Pages静态词典检索的预加载页面内容。

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

尽管 Xamarin.Forms 代码片段用于解释此方法,但此方法中没有任何内容不能在普通的 UWP 应用中使用。

最新更新