WinUI 3:点击NavigationViewItem自动跳转至子项的实现问题
问题背景
我创建了一个包含多个项及带子菜单项的NavigationView,相关XAML代码如下:
<NavigationView.MenuItems> <NavigationViewItem x:Uid="Home" helpers:NavigationHelper.NavigateTo="Program.ViewModel.HomeViewModel"> </NavigationViewItem> <NavigationViewItem x:Uid="List" helpers:NavigationHelper.NavigateTo="Program.ViewModel.ListViewModel"> </NavigationViewItem> <NavigationViewItem x:Uid="Details" helpers:NavigationHelper.NavigateTo="Program.ViewModel.DetailsViewModel"> <NavigationViewItem.MenuItems> <NavigationViewItem x:Name="SummaryViewItem" x:Uid="Details_Summary" helpers:NavigationHelper.NavigateTo="Program.ViewModel.SummaryViewModel"> </NavigationViewItem> <NavigationViewItem x:Uid="Details_Distances" helpers.NavigationHelpers.NavigateTo="Program.ViewModel.DistancesViewModel"> </NavigationViewItem> <NavigationViewItem x:Uid="Details_Location" helpers.NavigationHelper.NavigateTo="Program.ViewModel.LocationViewModel"> </NavigationViewItem> </NavigationViewItem.MenuItems> </NavigationViewItem> </NavigationView.MenuItems>
其中DetailsViewModel负责为子页面收集数据,而Details页面本身为空;子页面包含视图和字段用于规整展示信息。需求是:当用户点击Details导航项时,程序先执行对应的ViewModel的数据收集逻辑,再自动导航至Summary页面。
尝试的实现
我在DetailsViewModel中添加了如下代码:
public async void OnNavigatedToAsync(object parameter) { //Other functions NavigationHelper.SetNavigateTo(ShellPage.SummaryNavigationItem, typeof(SummaryViewModel).FullName); }
并在Shell的代码后置中添加代码以暴露SummaryViewItem:
public static NavigationViewItem SummaryNavigationItem; public ShellPage(ShellViewModel viewModel) { SummaryNavigationItem = SummaryViewItem; }
但当前效果是仍显示空白的Details页面,并未导航至Summary页面。
补充信息:NavigationHelper代码(复制自WinUI 3示例应用):
public static void SetNavigateTo(NavigationViewItem item, string value) => item.SetValue(NavigateToProperty, value); public static readonly DependencyProperty NavigateToProperty = DependencyProperty.RegisterAttached("NavigateTo", typeof(string), typeof(NavigationHelper), new PropertyMetadata(null));
解决方案
你的核心问题是只设置了SummaryViewItem的NavigateTo属性,但没有主动触发导航操作;同时直接在ViewModel中操作UI元素也不符合MVVM规范。以下是两种可行的实现方式:
方式一:直接触发导航并同步选中状态
在DetailsViewModel完成数据收集后,调用导航服务跳转到SummaryViewModel,同时更新NavigationView的选中项:
public async void OnNavigatedToAsync(object parameter) { // 执行数据收集逻辑,确保异步操作完成 await CollectSubPageData(); // 获取项目中的导航服务实例(根据实际结构调整,比如依赖注入或静态引用) var navigationService = App.GetService<INavigationService>(); // 导航到SummaryViewModel await navigationService.NavigateToAsync(typeof(SummaryViewModel).FullName); // 同步NavigationView的选中状态 if (ShellPage.SummaryNavigationItem != null && ShellPage.Instance.MainNavigationView != null) { ShellPage.Instance.MainNavigationView.SelectedItem = ShellPage.SummaryNavigationItem; } }
注意:需要在ShellPage中添加公开的NavigationView引用:
public NavigationView MainNavigationView { get; private set; } public static ShellPage Instance { get; private set; } public ShellPage(ShellViewModel viewModel) { InitializeComponent(); Instance = this; MainNavigationView = this.FindName("MainNavView") as NavigationView; // 延迟获取子项引用,避免初始化时为空 Loaded += (s, e) => SummaryNavigationItem = SummaryViewItem; }
方式二:基于MVVM的消息通知实现(更优雅)
- 在
ShellViewModel中维护SelectedNavigationItem属性,绑定到NavigationView:
<NavigationView x:Name="MainNavView" SelectedItem="{x:Bind ViewModel.SelectedNavigationItem, Mode=TwoWay}">
private NavigationViewItem _selectedNavigationItem; public NavigationViewItem SelectedNavigationItem { get => _selectedNavigationItem; set => SetProperty(ref _selectedNavigationItem, value); }
- 使用CommunityToolkit.Mvvm的消息机制,在
DetailsViewModel完成数据后发送切换消息:
// DetailsViewModel using CommunityToolkit.Mvvm.Messaging; public async void OnNavigatedToAsync(object parameter) { await CollectSubPageData(); // 发送导航切换消息 WeakReferenceMessenger.Default.Send(new NavigateToSummaryMessage()); } // 定义消息类 public class NavigateToSummaryMessage { }
- 在
ShellViewModel中注册消息,处理导航切换:
using CommunityToolkit.Mvvm.Messaging; public ShellViewModel() { WeakReferenceMessenger.Default.Register<NavigateToSummaryMessage>(this, (recipient, message) => { // 设置选中SummaryViewItem,自动触发导航(依赖NavigationView的SelectionChanged事件绑定) SelectedNavigationItem = ShellPage.SummaryNavigationItem; }); }
- 确保NavigationView的
SelectionChanged事件绑定了导航逻辑:
private void MainNavView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args) { if (args.SelectedItem is NavigationViewItem item) { var navigateTo = NavigationHelper.GetNavigateTo(item); if (!string.IsNullOrEmpty(navigateTo)) { _navigationService.NavigateToAsync(navigateTo); } } }
关键注意事项
- 避免在ViewModel中直接操作UI元素,优先使用绑定或消息机制,遵循MVVM设计原则。
- 确保数据收集的异步操作完成后再触发导航,使用
await等待任务结束。 - 延迟获取
SummaryViewItem的引用(比如在Loaded事件中),避免初始化时元素尚未加载导致空引用。
内容的提问来源于stack exchange,提问作者MohsenHNSJ
相关产品推荐
相关产品推荐

