UWP中动态绑定NavigationViewItem与NavigationViewItemHeader问题
你的核心问题在于错误地将Grid和ListView直接嵌套进了NavigationView.MenuItems中——这个集合只接受NavigationViewItem、NavigationViewItemHeader、NavigationViewItemSeparator这类导航专用控件,ListView会被当作单个导航项的内容,自然只会占用单个项的空间。下面给你两种可行的解决方案,推荐用数据绑定的方式,更符合UWP的开发习惯。
方案一:数据绑定+模板选择器(推荐,MVVM友好)
这种方式通过定义数据模型,把菜单数据和UI分离,便于维护和扩展。
1. 定义导航项数据模型
先创建一个类来区分导航头部和普通导航项:
public class NavigationMenuItem { // 显示文本 public string Title { get; set; } // 是否是头部 public bool IsHeader { get; set; } // 导航项图标(可选) public Symbol Icon { get; set; } // 页面标记,用于导航时区分目标页面 public string PageTag { get; set; } }
2. 准备菜单数据集合
在页面或ViewModel中创建可观察集合,并加载你的书籍数据:
using System.Collections.ObjectModel; public ObservableCollection<NavigationMenuItem> MenuItems { get; } = new ObservableCollection<NavigationMenuItem>(); private void LoadMenuItems() { // 添加"My Books"头部 MenuItems.Add(new NavigationMenuItem { Title = "My Books", IsHeader = true }); // 从你的books集合中加载书籍导航项 foreach (var book in books.getBooks()) { MenuItems.Add(new NavigationMenuItem { Title = book.Name, IsHeader = false, Icon = Symbol.Book, PageTag = book.Id.ToString() }); } }
3. 创建模板选择器区分头部和项
创建一个模板选择器,让NavigationView知道不同数据该用哪种UI模板:
using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; public class NavigationMenuTemplateSelector : DataTemplateSelector { public DataTemplate HeaderTemplate { get; set; } public DataTemplate ItemTemplate { get; set; } protected override DataTemplate SelectTemplateCore(object item) { if (item is NavigationMenuItem menuItem) { return menuItem.IsHeader ? HeaderTemplate : ItemTemplate; } return base.SelectTemplateCore(item); } }
4. 修改XAML代码
在页面资源中定义模板和选择器,然后绑定NavigationView的数据源:
<Page.Resources> <!-- 导航头部模板 --> <DataTemplate x:Key="NavigationHeaderTemplate"> <NavigationViewItemHeader Content="{Binding Title}" /> </DataTemplate> <!-- 普通导航项模板 --> <DataTemplate x:Key="NavigationItemTemplate"> <NavigationViewItem Content="{Binding Title}" Tag="{Binding PageTag}"> <NavigationViewItem.Icon> <SymbolIcon Symbol="{Binding Icon}" /> </NavigationViewItem.Icon> </NavigationViewItem> </DataTemplate> <!-- 模板选择器 --> <local:NavigationMenuTemplateSelector x:Key="MenuTemplateSelector" HeaderTemplate="{StaticResource NavigationHeaderTemplate}" ItemTemplate="{StaticResource NavigationItemTemplate}" /> </Page.Resources> <!-- 修改后的NavigationView --> <NavigationView x:Name="NavViewTester" ItemInvoked="NavViewTester_ItemInvoked" Loaded="NavViewTester_Loaded" IsPaneToggleButtonVisible="True" IsPaneOpen="True" MenuItemsSource="{x:Bind MenuItems}" ItemTemplateSelector="{StaticResource MenuTemplateSelector}"> <!-- 导航内容区域,一般放Frame用于页面跳转 --> <Frame x:Name="ContentFrame" /> </NavigationView>
5. 加载数据并处理导航
在页面Loaded事件中加载菜单数据,然后处理ItemInvoked事件实现导航:
private void NavViewTester_Loaded(object sender, RoutedEventArgs e) { LoadMenuItems(); } private void NavViewTester_ItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args) { if (args.InvokedItemContainer is NavigationViewItem item && item.Tag is string tag) { // 根据tag跳转到对应的页面,示例: switch (tag) { case "book1": ContentFrame.Navigate(typeof(BookDetailPage), tag); break; // 其他书籍的跳转逻辑 } } }
方案二:代码后台动态创建控件(快速实现)
如果不想用数据绑定,也可以直接在后台代码中创建导航控件并添加到MenuItems:
修改XAML
清空MenuItems的内容,只保留基础的NavigationView:
<NavigationView x:Name="NavViewTester" ItemInvoked="NavViewTester_ItemInvoked" Loaded="NavViewTester_Loaded" IsPaneToggleButtonVisible="True" IsPaneOpen="True"> <Frame x:Name="ContentFrame" /> </NavigationView>
在Loaded事件中动态添加项
private void NavViewTester_Loaded(object sender, RoutedEventArgs e) { // 添加头部 var header = new NavigationViewItemHeader { Content = "My Books" }; NavViewTester.MenuItems.Add(header); // 添加书籍导航项 foreach (var book in books.getBooks()) { var navItem = new NavigationViewItem { Content = book.Name, Tag = book.Id.ToString(), Icon = new SymbolIcon(Symbol.Book) }; NavViewTester.MenuItems.Add(navItem); } }
导航事件的处理和方案一一致,这里就不重复了。
内容的提问来源于stack exchange,提问作者J.Carden
相关产品推荐
相关产品推荐

