教程 - 使用 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 in Desktop) 模板创建新的 C# 库项目。 在本演练中,我们将库项目命名为 WinUIComponentCs,将解决方案命名为 AuthoringWinUI

    请保持「将解决方案和项目放在同一目录中」 选项框未选中(否则,上一部分中针对 C++ 应用程序的 packages 文件夹将最终干扰 C# 库项目)。

    “新建库”对话框

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

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

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

    二. 搜索 Microsoft.Windows.CsWinRT NuGet 包并安装最新版本。

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

    <PropertyGroup>   
        <CsWinRTComponent>true</CsWinRTComponent>
    </PropertyGroup>
    
    • CsWinRTComponent 属性指定项目是 Windows 运行时组件, .winmd 以便在生成项目时生成文件。
  5. 将自定义控件或用户控件添加到库中。 为此,请在 Visual Studio 中右键单击项目,单击“ 添加新>”,然后在左窗格中选择 “WinUI ”。 在本演练中,我们添加了一个新的 用户控件(WinUI 3), 并将其命名 NameReporter.xamlNameReporter 用户控件允许用户在相应的 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 App 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中,添加以下可激活类注册。 还需要为 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 文件。

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

    xmlns:custom="using:WinUIComponentCs"
    

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

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

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

已知问题

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