WinUI 3中如何避免NavigationViewItem.Tag类型传参拼写错误?
问题描述
尝试在XAML中给NavigationViewItem的Tag属性传递Type类型参数,最初编写代码如下:
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:page="using:MyApp.Pages" > <NavigationView> <NavigationView.MenuItems> <NavigationViewItem Content="Test page" Tag="{x:Type page:TestPage}" Icon="Library" /> </NavigationView.MenuItems> <Frame x:Name="contentFrame" /> </NavigationView> </Page>
但触发错误:The Type x:Type was not found.
随后自定义了MarkupExtension尝试解决:
using Microsoft.UI.Xaml.Markup; using System; namespace MyApp.Xaml { [MarkupExtensionReturnType(ReturnType = typeof(Type))] public sealed class TypeExtension : MarkupExtension { public Type Type { get; set; } public TypeExtension(Type type) { Type = type; } protected override object ProvideValue() => Type; } }
使用自定义扩展的XAML代码:
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:t="using:MyApp.Xaml" xmlns:page="using:MyApp.Pages" > <NavigationView > <NavigationView.MenuItems> <NavigationViewItem Content="Test page" Tag="{t:Type page:TestPage}" Icon="Library" /> </NavigationView.MenuItems> <Frame x:Name="contentFrame" /> </NavigationView> </Page>
依旧报错:
Error WMC0100 XAML TypeExtension type cannot be constructed. In order to be constructed in XAML, a type cannot be abstract, interface, nested, generic or a struct, and must have a public default constructor.
需求为解决Tag传递Type的问题,或提供其他避免类名拼写错误的方案,对应的选择变更处理函数如下:
private void MainNavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args) { if (args.SelectedItem is NavigationViewItem item && item.Tag is Type pageType) { contentFrame.Navigate(pageType); } }
解决方案
方案1:修正自定义TypeExtension扩展
原扩展报错的核心原因是缺少无参公共构造函数,且XAML无法直接传递Type类型的构造参数。修改扩展为接收字符串类型的类型名称,再解析为Type:
using Microsoft.UI.Xaml.Markup; using System; using System.Reflection; namespace MyApp.Xaml { [MarkupExtensionReturnType(ReturnType = typeof(Type))] public sealed class TypeExtension : MarkupExtension { // 用字符串接收类型的完全限定名或短名 public string TypeName { get; set; } // 必须添加无参公共构造函数 public TypeExtension() { } public TypeExtension(string typeName) { TypeName = typeName; } protected override object ProvideValue() { if (string.IsNullOrEmpty(TypeName)) throw new ArgumentNullException(nameof(TypeName)); // 优先从当前程序集查找类型 var type = Assembly.GetExecutingAssembly().GetType(TypeName); if (type == null) { // 处理XAML命名空间别名(如page:TestPage) var parts = TypeName.Split(':'); if (parts.Length == 2) { // 根据XAML中的命名空间映射拼接完全限定名 type = Type.GetType($"MyApp.Pages.{parts[1]}"); } } return type ?? throw new InvalidOperationException($"无法找到类型 {TypeName}"); } } }
使用方式不变,仍支持两种写法:
<!-- 使用XAML命名空间别名 --> <NavigationViewItem Content="Test page" Tag="{t:Type page:TestPage}" Icon="Library" /> <!-- 使用完全限定名 --> <NavigationViewItem Content="Test page" Tag="{t:Type MyApp.Pages.TestPage}" Icon="Library" />
方案2:字符串存储类型名+编译时检查
如果不想自定义扩展,可在Tag中存储类型的完全限定名,通过静态常量类避免拼写错误:
- 创建静态常量类:
namespace MyApp.Constants { public static class PageTypeNames { // 用nameof确保编译时检查类型名 public const string TestPage = nameof(MyApp.Pages.TestPage); // 其他页面类型常量... } }
- XAML中引用常量:
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:consts="using:MyApp.Constants" > <NavigationView> <NavigationView.MenuItems> <NavigationViewItem Content="Test page" Tag="{x:Static consts:PageTypeNames.TestPage}" Icon="Library" /> </NavigationView.MenuItems> <Frame x:Name="contentFrame" /> </NavigationView> </Page>
- 修改后台处理函数:
private void MainNavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args) { if (args.SelectedItem is NavigationViewItem item && item.Tag is string typeName) { var pageType = Type.GetType(typeName); if (pageType != null) { contentFrame.Navigate(pageType); } } }
方案3:绑定静态属性(强类型最优解)
创建静态类暴露页面类型的静态属性,直接绑定到Tag,完全实现强类型编译检查:
- 定义静态类:
namespace MyApp.ViewModels { public static class PageTypes { public static Type TestPage => typeof(MyApp.Pages.TestPage); // 其他页面类型属性... } }
- XAML中绑定静态属性:
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:vm="using:MyApp.ViewModels" > <NavigationView> <NavigationView.MenuItems> <NavigationViewItem Content="Test page" Tag="{x:Static vm:PageTypes.TestPage}" Icon="Library" /> </NavigationView.MenuItems> <Frame x:Name="contentFrame" /> </NavigationView> </Page>
这种方式无需修改原有的后台处理函数,编译时即可检查类型是否存在,彻底避免拼写错误。
内容的提问来源于stack exchange,提问作者Milan Majer
相关产品推荐
相关产品推荐

