You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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中存储类型的完全限定名,通过静态常量类避免拼写错误:

  1. 创建静态常量类:
namespace MyApp.Constants
{
    public static class PageTypeNames
    {
        // 用nameof确保编译时检查类型名
        public const string TestPage = nameof(MyApp.Pages.TestPage);
        // 其他页面类型常量...
    }
}
  1. 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>
  1. 修改后台处理函数:
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,完全实现强类型编译检查:

  1. 定义静态类:
namespace MyApp.ViewModels
{
    public static class PageTypes
    {
        public static Type TestPage => typeof(MyApp.Pages.TestPage);
        // 其他页面类型属性...
    }
}
  1. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 17:01:24