windows C++-使用 C++/WinRT 创作 API(二)

可扩展应用程序标记语言(XAML)是一种声明性语言。 具体来讲,XAML 可初始化对象和设置对象的属性,使用一种可显示多个对象间分层关系的语言结构,还使用了一种支持类型扩展的支持类型约定。 可以在声明性 XAML 标记中创建可见的 UI 元素。 然后,可以为每个 XAML 文件关联单独的代码隐藏文件,该文件可以响应事件并操作最初在 XAML 中声明的对象。

在开发过程中,XAML 语言支持不同工具和角色之间的源代码交换,例如在设计工具与交互式开发环境 (IDE) 或是主开发人员与本地化开发人员之间交换 XAML 源代码。 通过使用 XAML 作为交换格式,设计器角色和开发人员角色可以保持独立或组合在一起,设计器和开发人员可以在应用生产期间循环访问。

在Windows 运行时应用项目中看到它们时,XAML 文件是扩展名为 .xaml 文件扩展名的 XML 文件。

XAML 具有基于 XML 的基本语法。 根据定义,有效的 XAML 也必须是有效的 XML。 但 XAML 也拥有可赋予不同且更加完整含义的语法概念,根据 XML 1.0 规范,它在 XML 中也有效。 例如,XAML 支持 属性元素语法,其中可以在元素中设置属性值,而不是属性或内容中的字符串值。 对于常规 XML,XAML 属性元素是名称中包含点的元素,因此对纯 XML 有效,但含义不相同。

Microsoft Visual Studio 可帮助生成有效的 XAML 语法,无论是在 XAML 文本编辑器中,还是更图形化的 XAML 设计图面。 在你使用 Visual Studio 为应用编写 XAML 时,不必时时担心语法问题。 IDE 鼓励通过提供自动完成提示、在 Microsoft IntelliSense 列表和下拉列表中显示建议、在“工具箱”窗口中显示 UI 元素库或其他技术等方式,来编写有效的 XAML 语法。

如果你正在创作要在 XAML UI 中引用的运行时类

如果类型将由 XAML UI 引用,则它需要是一个运行时类,即使它与 XAML 是在同一项目中。 尽管它们通常是跨可执行文件边界进行激活的,但在实现它的编译单元内可以改用一个运行时类。

在此情况下,既会创作 API 同时又会使用 API 。 实现运行时类的过程与实现 Windows 运行时组件的过程实质上是相同的。 唯一不同的细节在于,从 IDL 中,C++/WinRT 工具链不仅生成一个实现类型,而且还生成一个具现类型。 在此情况下只说明“MyRuntimeClass”可能是不明确的,认识到这一点很重要,因为有多个具有该名称的不同类型的实体 。

  • MyRuntimeClass 是运行时类的名称 。 但这实际上是一种抽象:在 IDL 中声明并以某种编程语言实现;
  • MyRuntimeClass 是 C++ 结构 winrt::MyProject::implementation::MyRuntimeClass(运行时类的 C++/WinRT 实现)的名称 。 正如我们看到的,如果有不同的实现和使用项目,则此结构仅存在于实现项目中。 这是实现类型或实现 。 此类型由 cppwinrt.exe 工具在文件 \MyProject\MyProject\Generated Files\sources\MyRuntimeClass.h 和 MyRuntimeClass.cpp 中生成;
  • MyRuntimeClass 是采用 C++ 结构 winrt::MyProject::MyRuntimeClass 形式的具现类型的名称 。 如果有不同的实现和使用项目,则此结构仅存在于使用项目中。 这是具现类型 。 此类型由 cppwinrt.exe 在文件 \MyProject\MyProject\Generated Files\winrt\impl\MyProject.2.h 中生成;

 下面是与本文相关的具现类型部分。

// MyProject.2.h
...
namespace winrt::MyProject
{
    struct MyRuntimeClass : MyProject::IMyRuntimeClass
    {
        MyRuntimeClass(std::nullptr_t) noexcept {}
        MyRuntimeClass();
    };
}
 将运行时类重构到 Midl 文件 (.idl) 中

Visual Studio 项目和项模板为每个运行时类生成一个单独的 IDL 文件。 这样就可以在 IDL 文件及其生成的源代码文件之间形成逻辑对应关系。

不过,如果将项目的所有运行时类合并成单个 IDL 文件,则可显著缩短生成时间。 若要在它们之间建立复杂的(或循环的)import 依赖关系,则合并操作实际上是必需的。 你会发现,如果将运行时类放置在一起,则创作和查看它们会更容易。

 运行时类构造函数

关于我们在上面看到的列表,要记住下面这些要点。

  • 在 IDL 中声明的每个构造函数都会使得在实现类型和具现类型上生成一个构造函数。 将通过 IDL 声明的构造函数来使用不同编译单元中的运行时类 ;
  • 无论你是否有 IDL 声明的构造函数,在具现类型上都会生成一个接受 std::nullptr_t 的构造函数重载。 从同一编译单元中使用运行时类需要两个步骤,调用 std::nullptr_t 构造函数便是第一步 。 ;
  • 如果你正在从同一编译单元使用运行时类,则还可以在实现类型上(请记住,这是在 MyRuntimeClass.h 中)直接实现非默认构造函数 ;

如果你预计运行时类将会从不同的编译单元使用(这很常见),则应在 IDL 中包括相应的构造函数(至少包括一个默认构造函数)。 这样一来,你在获得实现类型的同时还将获得一个工厂实现。

如果你只想在同一编译单元内创作和使用运行时类,则不用在 IDL 中声明任何构造函数。 你不需要工厂实现,也不会生成它。 实现类型的默认构造函数将会被删除,但你可以轻松地编辑它并使之成为默认构造函数。

如果你只想在同一编译单元内创作和使用运行时类,并且需要构造函数参数,则应直接在实现类型上创作你所需的构造函数。

运行时类方法、属性和事件

我们已经看到,工作流要使用 IDL 来声明运行时类及其成员,然后工具会生成原型和存根实现。 至于为运行时类的成员自动生成的那些原型,你可以 编辑它们,以便其传送与你在 IDL 中声明的类型不同的类型。 但仅当你在 IDL 中声明的类型可转发到你在已实现的版本中声明的类型时,才可以这么做。例如: 

  • 可以放宽对参数类型的要求。 例如,如果在 IDL 中,你的方法接受 SomeClass,那么可以选择在实现中将其更改为 IInspectable。 这会起作用,因为任何 SomeClass 均可转发到 IInspectable(当然,反之则不然);
  • 可以按值(而不是按引用)接受可复制的参数。 例如,将 SomeClass const& 更改为 SomeClass。 这在你需要避免将引用捕获到协同例程时是必要的;
  • 可以放宽对返回值的要求。 例如,可以将 void 更改为 winrt::fire_and_forget;


编写异步事件处理程序时,最后两个都非常有用。

  • 20
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值