如何在代码中创建XAML自定义控件?

IIn*_*ble 6 uwp c++-winrt

我正在尝试使用 C++/WinRT 在代码中实现自定义 XAML 控件。然而,我尝试的实现未能编译。作为概念证明,我使用了这段代码:

#pragma once

#include <winrt/Windows.UI.Xaml.Controls.h>

namespace MyApp
{
    struct MyControl : winrt::implements<MyControl, winrt::Windows::UI::Xaml::Controls::Control>
    {
    };
}
Run Code Online (Sandbox Code Playgroud)

这导致了以下编译器错误:

1>MyControl.cpp
1>c:\program files (x86)\windows kits\10\include\10.0.17134.0\cppwinrt\winrt\base.h(6416): error C2079: 'winrt::impl::producer<D,winrt::Windows::UI::Xaml::Controls::Control,void>::vtable' uses undefined struct 'winrt::impl::produce<D,I>'
1>        with
1>        [
1>            D=MyApp::MyControl
1>        ]
1>c:\program files (x86)\windows kits\10\include\10.0.17134.0\cppwinrt\winrt\base.h(7163): note: see reference to class template instantiation 'winrt::impl::producer<D,winrt::Windows::UI::Xaml::Controls::Control,void>' being compiled
1>        with
1>        [
1>            D=MyApp::MyControl
1>        ]
1>c:\xxx\mycontrol.h(8): note: see reference to class template instantiation 'winrt::implements<MyApp::MyControl,winrt::Windows::UI::Xaml::Controls::Control>' being compiled
Run Code Online (Sandbox Code Playgroud)

我无法理解编译器错误。显然,您无法像实现 Windows 运行时使用的其他类型一样实现 XAML 控件。

在代码中实现 XAML 自定义控件需要什么?

Rya*_*erd 6

WinRT 中的“继承”或“子类化”与 C++ 继承略有不同。因为这些是 COM 接口,所以当您对 WinRT 运行时类进行子类化时,您真正要做的是COM 聚合,并结合实现基本类型的可重写接口。由于 COM 聚合方面的原因,这比标准 C++ 继承要复杂得多,涉及所有委托/非委托、特殊构造等。这将是 WRL 中的一个主要难题,但 C++/CX 在下面做了很多编译器魔法。引擎盖将其抽象化。幸运的是,C++/WinRT 可以帮助您提供两种类型的抽象,而无需诉诸无形的魔法。

如果您正在编写不需要外部可见的类型(例如应用程序,而不是运行时组件),C++/WinRT 为此提供了方便的帮助器:

#pragma once

#include <winrt/Windows.UI.Xaml.Controls.h>

namespace MyApp
{
    struct MyControl : winrt::Windows::UI::Xaml::Controls::ControlT<MyControl>
    {
        void OnTapped(winrt::Windows::UI::Xaml::Input::TappedRoutedEventArgs const&);
    };
}
Run Code Online (Sandbox Code Playgroud)

该基本类型ControlT将正确构造聚合的基本Control实例并将基本方法委托给它,同时还实现“可重写”接口。这些可重写的方法都有一个占位符实现,默认调用基本方法,但您可以自己重写它们并获得自定义行为。

另一方面,如果您需要通过 IDL 创作一个投影类型:

namespace MyApp
{
  [default_interface]
  runtimeclass MyControl : Windows.UI.Xaml.Controls.Control
  {
    MyControl();
  };
}
Run Code Online (Sandbox Code Playgroud)

这将生成与上面的内置ControlT案例类似的脚手架,但也会投影您的类型。事实上,如果您检查此类型生成的文件(在本例中为 MyControl.gh),您会看到MyControlT所有连接的地方。

(注意:[default_interface]只有当您有一个空的、可构造的、密封的运行时类时,才需要该属性。添加成员后,midl 将合成默认接口,而无需任何其他引导。