简体   繁体   English

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

[英]How do I pre-load ShellPage during Activation in a WinUI 3 NavigationView Desktop project?

I'd like to pre-load the ShellPage in a WinUI 3 (v1.1.5) Desktop application.我想在 WinUI 3 (v1.1.5) 桌面应用程序中预加载ShellPage That is, during Activation (called by也就是说,在Activation期间(由

await App.GetService<IActivationService>().ActivateAsync(args);

in the OnLaunched handler of the App class), I'd like to make sure ShellPage is loaded before any of the navigation pages are displayed.App类的OnLaunched处理程序中),我想确保在显示任何导航页面之前加载ShellPage I've changed the service configuration to include我已将服务配置更改为包括

    services.AddSingleton<ShellPage>();
    services.AddSingleton<ShellViewModel>();

in the constructor for the App class which should mean only one of each of ShellPage and ShellViewModel will be instantiated for the app run but the question is when are they fully provisioned?App class 的构造函数中,这应该意味着只有ShellPageShellViewModel中的一个将为应用程序运行实例化,但问题是它们何时完全配置?

The normal progression is that the Activation step first assigns ShellPage to MainWindow.Content , then navigates to MainPage (these are the names for the default project).正常的进程是Activation步骤首先将ShellPage分配给MainWindow.Content ,然后导航到MainPage (这些是默认项目的名称)。 Because MainPage is actually loaded into a Frame on ShellPage , it seems layout for MainPage happens before ShellPage layout is completed.因为MainPage实际上是加载到ShellPage上的Frame中,所以MainPage的布局似乎发生在ShellPage布局完成之前。

Any idea how I do this on initial startup?知道我如何在初始启动时执行此操作吗? This is only an issue when the first Page is presented.这只是出现第一Page时的问题。 After that, ShellPage is reused.之后, ShellPage被重用。

By default, TemplateStudio 's navigation re-instantiates pages for every navigation.默认情况下, TemplateStudio的导航会为每个导航重新实例化页面。 It doesn't use the ServicesProvider , so registering your pages as singleton won't help.它不使用ServicesProvider ,因此将您的页面注册为 singleton 无济于事。

If you want to keep your page instances, you need to set NavigationCacheMode="Required" on your pages.如果要保留页面实例,则需要在页面上设置NavigationCacheMode="Required" This way, your pages will be cached even after you navigate away.这样,即使在您离开后,您的页面也会被缓存。

Still, your pages won't be instantiated until you navigate to them once at least.尽管如此,至少在您导航到它们一次之前,您的页面不会被实例化。 In order to instantiate all of your pages at the very beginning, you need to navigate through them at least once.为了在一开始就实例化所有页面,您需要至少浏览一次。

You can get all the NavigationViewItem s with a method like this.您可以使用这样的方法获取所有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;
        }
    }
}

And use it like this in the NavigationViewService 's Initialize method.并在NavigationViewServiceInitialize方法中像这样使用它。

[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);
        }
    }
}

A little clarification first, and then the answer I found to the issue.首先澄清一下,然后是我找到的问题的答案。

Andrew's answer (above) is great for instantiating all of the Pages in the NavigationView at startup but the very first page loaded still would not have access to a fully loaded ShellPage in its constructor (and thus, a fully populated element tree). Andrew 的回答(上图)非常适合在启动时实例化NavigationView中的所有Pages ,但加载的第一个页面仍然无法访问其构造函数中完全加载的ShellPage (因此,完全填充的元素树)。 Andrew is right that the NavigationViewItems ( Pages ) don't persist by default, but the ShellPage does as it's part of the UI. Andrew 是正确的, NavigationViewItems ( Pages ) 默认情况下不会持久存在,但ShellPage确实存在,因为它是 UI 的一部分。 Specifically, it is the content of the MainWindow and defines a Frame into which NavigationViewItems are loaded.具体来说,它是MainWindow的内容,并定义了一个FrameNavigationViewItems将加载到该框架中。 Regardless of which Page is displayed, it's the same instance of the ShellPage people see.无论显示哪个Page ,人们看到的都是ShellPage的同一个实例。

The issue arises because of the order in which Activation (specifically, the DefaultActivationHandler ) is done at App startup.出现此问题是因为在App启动时完成Activation (特别是DefaultActivationHandler )的顺序。 When the App starts, it callsApp启动时,它会调用

await App.GetService<IActivationService>().ActivateAsync(args);

which does哪个

    // Set the MainWindow Content.
    if (App.MainWindow.Content == null)
    {
        _shell = App.GetService<ShellPage>();
        App.MainWindow.Content = _shell ?? new Frame();
    }

and navigates to the first Page (loads the first Page into the NavigationView.Frame by calling DefaultActivationHandler ) before finishing the loading of ShellPage .并在完成ShellPage的加载之前导航到第一Page (通过调用DefaultActivationHandler将第一Page加载到NavigationView.Frame中)。 Thus, ShellPage is not fully loaded ( ShellPage.IsLoaded == false ) when MainPage is loaded.因此,加载MainPage时, ShellPage未完全加载 ( ShellPage.IsLoaded == false )。

To fully instantiate ShellPage before any of the NavigationViewItem Pages are loaded, simply change the loading sequence.要在加载任何NavigationViewItem Pages之前完全实例化ShellPage ,只需更改加载顺序即可。 First, defer the navigation to the first page (whichever you choose) by editing HandleInternalAsync in DefaultActivationHandler.cs to首先,通过在DefaultActivationHandler.cs中编辑HandleInternalAsync将导航延迟到第一页(无论您选择哪个)

    protected async override Task HandleInternalAsync(LaunchActivatedEventArgs args)
    {
        //_navigationService.NavigateTo(typeof(MainViewModel).FullName!, args.Arguments);

        await Task.CompletedTask;
    }

Move the navigation to the OnLoaded handler in ShellPage.xaml.cs :将导航移动到ShellPage.xaml.cs中的OnLoaded处理程序:

    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!);
    }

All Pages now receive a loaded ShellPage when navigated to, regardless of order.现在,无论顺序如何,所有Pages在导航到时都会收到加载的ShellPage

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM