我想在 WinUI 3 (v1.1.5) 桌面应用程序中预加载
ShellPage
。也就是说,在 Activation
期间(由
await App.GetService<IActivationService>().ActivateAsync(args);
在
OnLaunched
类的 App
处理程序中),我想确保在显示任何导航页面之前加载 ShellPage
。我已更改服务配置以包含
services.AddSingleton<ShellPage>();
services.AddSingleton<ShellViewModel>();
在
App
类的构造函数中,这应该意味着 ShellPage
和 ShellViewModel
中的每一个都将为应用程序运行实例化,但问题是它们何时完全配置?
正常的进程是
Activation
步骤首先将 ShellPage
分配给 MainWindow.Content
,然后导航到 MainPage
(这些是默认项目的名称)。因为 MainPage
实际上加载到 Frame
上的 ShellPage
中,所以 MainPage
的布局似乎发生在 ShellPage
布局完成之前。
知道我在初次启动时如何执行此操作吗?仅当出现第一个
Page
时,这才是问题。之后,ShellPage
将被重复使用。
首先稍微澄清一下,然后是我找到的问题答案。
Andrew 的答案(上面)非常适合在启动时实例化
Pages
中的所有 NavigationView
,但加载的第一个页面仍然无法在其构造函数中访问完全加载的 ShellPage
(因此,一个完全加载的页面)填充元素树)。 Andrew 是对的,NavigationViewItems
(Pages
) 默认情况下不会持续存在,但 ShellPage
会持续存在,因为它是 UI 的一部分。具体来说,它是 MainWindow
的内容,并定义了 Frame
,将 NavigationViewItems
加载到其中。无论显示哪个 Page
,它都是人们看到的 ShellPage
的同一个实例。
出现此问题的原因在于
Activation
(具体来说,DefaultActivationHandler
)在 App
启动时完成的顺序。当 App
启动时,它会调用
await App.GetService<IActivationService>().ActivateAsync(args);
确实如此
// Set the MainWindow Content.
if (App.MainWindow.Content == null)
{
_shell = App.GetService<ShellPage>();
App.MainWindow.Content = _shell ?? new Frame();
}
并导航到第一个
Page
(通过调用Page
将第一个NavigationView.Frame
加载到DefaultActivationHandler
中)在完成加载ShellPage
之前。因此,当 ShellPage
加载时,ShellPage.IsLoaded == false
未完全加载 (MainPage
)。
要在加载任何
ShellPage
NavigationViewItem
之前完全实例化 Pages
,只需更改加载顺序即可。首先,通过将 HandleInternalAsync
中的 DefaultActivationHandler.cs
编辑为,将导航推迟到第一页(无论您选择哪个)
protected async override Task HandleInternalAsync(LaunchActivatedEventArgs args)
{
//_navigationService.NavigateTo(typeof(MainViewModel).FullName!, args.Arguments);
await Task.CompletedTask;
}
将导航移至
OnLoaded
中的 ShellPage.xaml.cs
处理程序:
private void OnLoaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
TitleBarHelper.UpdateTitleBar(RequestedTheme);
KeyboardAccelerators.Add(BuildKeyboardAccelerator(VirtualKey.Left, VirtualKeyModifiers.Menu));
KeyboardAccelerators.Add(BuildKeyboardAccelerator(VirtualKey.GoBack));
App.GetService<INavigationService>().NavigateTo(typeof(MainViewModel).FullName!);
}
所有
Pages
现在在导航到时都会收到已加载的 ShellPage
,无论顺序如何。
默认情况下,TemplateStudio的导航会重新实例化每个导航的页面。它不使用
ServicesProvider
,因此将您的页面注册为单例不会有帮助。
如果您想保留页面实例,您需要在页面上将 NavigationCacheMode 设置为 Required。这样,即使您离开后,您的页面也会被缓存。
不过,在您至少导航到页面一次之前,您的页面不会被实例化。为了从一开始就实例化所有页面,您需要至少浏览它们一次。
你可以用这样的方法得到所有的
NavigationViewItem
。
private static IEnumerable<NavigationViewItem> GetNavigationViewItems(IEnumerable<object> items)
{
foreach (var item in items.OfType<NavigationViewItem>())
{
yield return item;
foreach (var grandChild in GetNavigationViewItems(item.MenuItems.OfType<NavigationViewItem>()))
{
yield return grandChild;
}
}
}
并在
NavigationViewService
的Initialize
方法中像这样使用它。
[MemberNotNull(nameof(_navigationView))]
public void Initialize(NavigationView navigationView)
{
_navigationView = navigationView;
_navigationView.BackRequested += OnBackRequested;
_navigationView.ItemInvoked += OnItemInvoked;
IEnumerable<NavigationViewItem> menuItems =
GetNavigationViewItems(_navigationView.MenuItems);
foreach (var item in menuItems)
{
if (item.GetValue(NavigationHelper.NavigateToProperty) is string pageKey)
{
_navigationService.NavigateTo(pageKey);
}
}
}