等待页面加载 - 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
导航太快了。
在您提到的评论中,您主要希望 WeekID
在 Tags_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
的值。小费。
注意:我认为这不是解决我的问题的好方法,但它确实有效。
这是我所做的:
- 在
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
需要考虑的事项:如果您可以 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 正在执行以下操作:
- 他们的导航扩展功能获取 pre-generated 页面并将其放入静态字典 (
FormsEmbeddedPageWrapper.Pages
),键入新生成的 GUID。
- UWP
frame.Navigate(Type, object)
方法使用非常轻量级的 FormsEmbeddedPageWrapper
类型和上面生成的 GUID 密钥作为参数调用。
- UWP 实例化并显示
FormsEmbeddedPageWrapper
的新实例。
- UWP 在
FormsEmbeddedPageWrapper
实例上调用 OnNavigatedTo
- 并在参数中包含 GUID 键。
FormsEmbeddedPageWrapper
实例使用 GUID 键从 Pages
静态字典中提取 pre-loaded 页面内容。
FormsEmbeddedPageWrapper
将其 ContentPresenter
的 Content
设置为从 Pages
静态字典中检索到的 pre-loaded 页面内容。
这种方法保留了 UWP 页面转换动画的使用,并允许 pre-load 编辑页面内容。
尽管 Xamarin.Forms 代码片段用于解释此方法,但此方法中没有任何内容不能在 run-of-the-mill 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
导航太快了。
在您提到的评论中,您主要希望 WeekID
在 Tags_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
的值。小费。
注意:我认为这不是解决我的问题的好方法,但它确实有效。
这是我所做的:
- 在
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
需要考虑的事项:如果您可以 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 正在执行以下操作:
- 他们的导航扩展功能获取 pre-generated 页面并将其放入静态字典 (
FormsEmbeddedPageWrapper.Pages
),键入新生成的 GUID。 - UWP
frame.Navigate(Type, object)
方法使用非常轻量级的FormsEmbeddedPageWrapper
类型和上面生成的 GUID 密钥作为参数调用。 - UWP 实例化并显示
FormsEmbeddedPageWrapper
的新实例。 - UWP 在
FormsEmbeddedPageWrapper
实例上调用OnNavigatedTo
- 并在参数中包含 GUID 键。 FormsEmbeddedPageWrapper
实例使用 GUID 键从Pages
静态字典中提取 pre-loaded 页面内容。FormsEmbeddedPageWrapper
将其ContentPresenter
的Content
设置为从Pages
静态字典中检索到的 pre-loaded 页面内容。
这种方法保留了 UWP 页面转换动画的使用,并允许 pre-load 编辑页面内容。
尽管 Xamarin.Forms 代码片段用于解释此方法,但此方法中没有任何内容不能在 run-of-the-mill UWP 应用程序中使用。