演练 - 使用 WinUI 3 控件创建 C# 组件,并从使用 Windows 应用 SDK 的 C++/WinRT 应用中使用它

C#/WinRT 支持创作 Windows 运行时组件,包括 WinUI 自定义类型和自定义控件。 可以从使用 Windows 应用 SDK 的 C# 或 C++/WinRT 应用程序中使用这些组件。 建议使用 C#/WinRT v1.6.4 或更高版本创作具有 NuGet 打包支持的运行时组件。

有关支持的方案的更多详细信息,请参阅 C#/WinRT GitHub 存储库中的创作 C#/WinRT 组件

本演练演示如何使用自定义 WinUI 3 控件创作 C# 组件,以及如何从使用 Windows 应用 SDK 项目模板的 C++/WinRT 应用中使用该组件。

必备条件

本演练需要下列工具和组件:

使用 Windows 应用 SDK 创作 C#/WinRT 组件

  1. 使用由 Windows 应用 SDK 提供的类库 (桌面版 WinUI 3)创建新的 C# 库项目。 在本演练中,我们将库项目命名为 WinUIComponentCs,将解决方案命名为 AuthoringWinUI。

    取消选中“将解决方案和项目放在同一目录中”框(否则,上一节中 C++ 应用程序的 packages 文件夹最终会干扰 C# 库项目)。

    New library dialog

  2. 删除默认包含的 Class1.cs 文件。

  3. 将最新的 Microsoft.Windows.CsWinRT NuGet 包安装到项目中。

    i. 在“解决方案资源管理器”中,右键单击项目节点并选择“管理 NuGet 包”。

    ii. 搜索“Microsoft.Windows.CsWinRT”NuGet 包,并安装最新版本。

  4. 将以下属性添加到库项目:

    <PropertyGroup>   
        <CsWinRTComponent>true</CsWinRTComponent>
    </PropertyGroup>
    
    • CsWinRTComponent 属性指定项目是 Windows 运行时组件,因此生成项目时会生成 .winmd 文件。
  5. 将自定义控件或用户控件添加到库。 为此,请在 Visual Studio 中右键单击项目,单击“添加”>“新项”,然后在左窗格中选择“WinUI”。 在本演练中,我们添加了新的用户控件 (WinUI 3) 并将其命名为了 NameReporter.xaml。 NameReporter 用户控件允许用户在相应的 TextBox 控件中输入名字和姓氏,然后单击按钮。 然后,该控件将显示一个消息框,其中包含用户输入的名字。

  6. 将以下代码粘贴到 NameReporter.xaml 文件:

    <UserControl
    x:Class="WinUIComponentCs.NameReporter"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:WinUIComponentCs"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
    
        <StackPanel HorizontalAlignment="Center">
            <StackPanel.Resources>
                <Style x:Key="BasicTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BodyTextBlockStyle}">
                    <Setter Property="Margin" Value="10,10,10,10"/>
                </Style>
            </StackPanel.Resources>
    
            <TextBlock Text="Enter your name." Margin="0,0,0,10"/>
            <StackPanel Orientation="Horizontal" Margin="0,0,0,10">
                <TextBlock Style="{StaticResource BasicTextStyle}">
                    First Name:
                </TextBlock>
                <TextBox Name="firstName" />
            </StackPanel>
            <StackPanel Orientation="Horizontal" Margin="0,0,0,10">
                <TextBlock Style="{StaticResource BasicTextStyle}">
                    Last Name:
                </TextBlock>
                <TextBox Name="lastName" />
            </StackPanel>
            <Button Content="Submit" Click="Button_Click" Margin="0,0,0,10"/>
            <TextBlock Name="result" Style="{StaticResource BasicTextStyle}" Margin="0,0,0,10"/>
        </StackPanel>
    </UserControl>
    
  7. 将以下方法添加到 NameReporter.xaml.cs

    using System.Text;
    ...
    private void Button_Click(object sender, RoutedEventArgs e)
    {
        StringBuilder displayText = new StringBuilder("Hello, ");
        displayText.AppendFormat("{0} {1}.", firstName.Text, lastName.Text);
        result.Text = displayText.ToString();
    }
    
  8. 接下来即可生成 WinUIComponentCs 项目以为组件生成 .winmd 文件。

注意

还可将组件打包为供最终用户引用的 NuGet 包。 有关详细信息,请参阅 C#/WinRT Github 存储库上的创作 C#/WinRT 组件

从 Windows 应用 SDK C++/WinRT 应用引用组件

以下步骤演示如何从 C++/WinRT Windows 应用 SDK 应用程序使用上一部分创建的组件。 使用 C++ 中的 C#/WinRT 组件当前需要使用单项目打包的空白应用(桌面版 WinUI 3)模板。 请注意,还可从没有类注册的 C# 打包应用引用 C# 组件。

目前不支持使用单独的 Windows 应用程序打包 (WAP) 项目的打包应用。 有关受支持的项目配置的最新更新,请参阅 C#/WinRT GitHub 存储库中的创作 C#/WinRT 组件

  1. 将新的 C++ Windows 应用 SDK 应用程序项目添加到解决方案中。 在 Visual Studio 中右键单击解决方案,然后选择“添加”>“新项目”。 选择由 Windows 应用 SDK 提供的 C++ 打包的空白应用(桌面版 WinUI 3)模板。 在本演练中,我们将该应用命名为 CppApp。

  2. 将 C++ 应用的项目引用添加到 C# 组件。 在 Visual Studio 中,右键单击 C++ 项目,并选择“添加”>“引用”,然后选择 WinUIComponentCs 项目。

    注意

    支持将组件用作 NuGet 包引用,但存在一些限制。 也就是说,具有自定义用户控件的组件当前不能用作 NuGet 包引用。

  3. 在应用的 pch.h 头文件中,添加以下行:

    #include <winrt/WinUIComponentCs.h>
    #include <winrt/WinUIComponentCs.WinUIComponentCs_XamlTypeInfo.h>
    
  4. 打开程序包清单文件 Package.appxmanifest

    注意

    存在一个已知问题,即 Package.appxmanifest文件不会显示在Visual Studio 解决方案资源管理器中。 要解决此问题,请右键单击 C++ 项目,选择“卸载项目”,然后双击项目以打开 CppApp.vcxproj 文件。 将以下条目添加到项目文件,然后重新加载项目:

    <ItemGroup>
        <AppxManifest Include="Package.appxmanifest">
        <SubType>Designer</SubType>
        </AppxManifest>
    </ItemGroup>
    

    Package.appxmanifest 中,添加以下可激活类注册。 还需要为 WinUIComponentCs.WinUIComponentCs_XamlTypeInfo.XamlMetaDataProvider 类添加一个额外的 ActivatableClass 条目,才能激活 WinUI 类型。 右键单击 Package.appxmanifest 文件并选择“打开方式”>“XML (文本编辑器)”以编辑文件。

    <!--In order to host the C# component from C++, you must add the following Extension group and list the activatable classes-->
    <Extensions>
        <Extension Category="windows.activatableClass.inProcessServer">
            <InProcessServer>
                <Path>WinRT.Host.dll</Path>
                <ActivatableClass ActivatableClassId="WinUIComponentCs.NameReporter" ThreadingModel="both" />
                <ActivatableClass ActivatableClassId="WinUIComponentCs.WinUIComponentCs_XamlTypeInfo.XamlMetaDataProvider" ThreadingModel="both" />
            </InProcessServer>
        </Extension>
    </Extensions>
    
  5. 打开 MainWindow.xaml 文件。

    i. 添加对文件顶部组件命名空间的引用。

    xmlns:custom="using:WinUIComponentCs"
    

    ii. 将用户控件添加到现有 XAML 代码。

    <StackPanel>
        ...
        <custom:NameReporter/>
    </StackPanel>
    
  6. 将 CppApp 设置为启动项目 - 右键单击​​ CppApp,然后选择“设为启动项目”。 将解决方案配置设置为 x86。 在生成之前,可能还需要重新定位解决方案目标,以使用 Visual Studio 2022 生成工具进行生成。 右键单击解决方案,选择“重定目标解决方案”,并将平台工具集升级到 v143。

  7. 生成并运行应用以查看自定义 NameReporter 控件。

已知问题

  • 使用 C# 组件作为项目引用需要将 PublishReadyToRun 设置为 False。 有关更多详细信息,请参阅 Github 问题 #1151
  • 当前仅 x86 应用程序支持从 C++ 使用为 AnyCPU 构建的 C# 组件。 x64Arm64 应用会导致类似于以下内容的运行时错误:%1 不是有效的 Win32 应用程序。有关详细信息,请参阅 Github 问题 #1151