所以我有一个 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
的值。只是一个提示。
注意:我不认为这是解决我问题的好方法,但它是有效的。
这是我所做的:
- 在
Tags_SelectionChanged
中添加了对Top.Count
的检查,以便在该List
为空时不运行任何内容。 - 在
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 正在执行的操作:
- 他们的导航扩展函数采用预先生成的页面并将其放入静态字典 (
FormsEmbeddedPageWrapper.Pages
),键控到新生成的 GUID。 - UWP
frame.Navigate(Type, object)
方法使用非常轻量级的FormsEmbeddedPageWrapper
类型和上面生成的 GUID 密钥作为参数进行调用。 - UWP 实例化并显示
FormsEmbeddedPageWrapper
的新实例。 - UWP 调用
OnNavigatedTo
FormsEmbeddedPageWrapper
实例 - 并在参数中包含 GUID 密钥。 FormsEmbeddedPageWrapper
实例使用 GUID 键从Pages
静态字典中提取预加载的页面内容。-
FormsEmbeddedPageWrapper
将其ContentPresenter
的Content
设置为从Pages
静态词典检索的预加载页面内容。
此方法保留了 UWP 页面过渡动画的使用,并允许预加载页面内容。
尽管 Xamarin.Forms 代码片段用于解释此方法,但此方法中没有任何内容不能在普通的 UWP 应用中使用。