等待页面加载 - UWP

Waiting for page to load - UWP

所以我有一个 Windows 10 应用程序,它的 XAML 页面有一个 Pivot 项,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(); 方法完成 之前 我允许应用程序导航 Pivot 控件我的 XAML 布局。如果我不这样做,传入的参数 (Top) 将是 null/empty 并且我的应用程序将在我导航到 TopNewsPage.

时立即抛出异常

我该怎么做?

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

在您提到的评论中,您主要希望 WeekIDTags_SelectionChanged 起作用之前首先具有一个值,并且 WeekID 设置在 OnNavigatedTo 事件。只要 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 或空,因为我不知道 [=18] =] 将在此类事件中保留 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

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

是的,但有办法解决这个问题。首先,考虑 Page 继承自 FrameworkElement。这意味着 Page 可以是 Grid 的子项或 ContentPresenter 的内容。这是一个显示这一点的快速实验:

ChildPage.xaml(呈现前我要pre-load的页面):

<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(托管页面 - 将添加 pre-loaded 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。正如我们将看到的,这是放置 pre-loaded 内容的地方。

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

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

这种方法保留了 UWP 页面转换动画的使用,并允许 pre-load 编辑页面内容。

尽管 Xamarin.Forms 代码片段用于解释此方法,但此方法中没有任何内容不能在 run-of-the-mill UWP 应用程序中使用。