如何在 WinUI 3 NavigationView 桌面项目激活期间预加载 ShellPage?

问题描述 投票:0回答:2

我想在 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
将被重复使用。

c# loading startup navigationview winui-3
2个回答
3
投票

首先稍微澄清一下,然后是我找到的问题答案。

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
,无论顺序如何。


2
投票

默认情况下,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);
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.