Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Это важно
Основные понятия и термины, поддерживающие понимание использования и разработки классов среды выполнения с помощью C++/WinRT, см. в статье "Использование API с помощью C++/WinRT и разработки API с помощью C++/WinRT".
Одной из самых мощных функций Windows App SDK является гибкость, которую предоставляет стек пользовательского интерфейса для создания пользовательских элементов управления на основе типа элемента управления XAML. Платформа пользовательского интерфейса XAML предоставляет такие функции, как настраиваемые свойства зависимостей и присоединенные свойства, а также шаблоны элементов управления, которые упрощают создание расширенных функций и настраиваемых элементов управления. В этом разделе описаны шаги по созданию пользовательского (шаблонного) элемента управления с помощью C++/WinRT.
Создайте пустое приложение (BgLabelControlApp)
Начните с создания проекта в Microsoft Visual Studio. Создайте проект C++ Пустое упакованное приложение (WinUI 3 в Desktop), задайте ему имя BgLabelControlApp и, чтобы структура папок соответствовала пошаговому руководству, убедитесь, что флажок Разместить решение и проект в одном каталоге снят. Нацелимся на последнюю общедоступную версию (т. е. не предварительную) пакета SDK Windows.
В одном из следующих разделов этой темы вам будет предложено собрать проект (но до этого не собирайте его).
Note
Сведения о настройке Visual Studio для разработки C++/WinRT, включая установку и использование расширения C++/WinRT Visual Studio (VSIX) и пакета NuGet (которые вместе предоставляют поддержку шаблона проекта и сборки),см. Visual Studio поддержку C++/WinRT.
Мы создадим новый класс для представления пользовательского (шаблонного) элемента управления. Мы создадим и потребляем класс в одном модуле компиляции. Но мы хотим иметь возможность создать экземпляр этого класса из разметки XAML, и по этой причине он будет классом среды выполнения. И мы будем использовать C++/WinRT как для его создания, так и для его использования.
Первым шагом в создании нового класса среды выполнения является добавление нового элемента Midl File (IDL) в проект. Назовите его BgLabelControl.idl. Удалите содержимое BgLabelControl.idl по умолчанию и вставьте следующее объявление класса среды выполнения.
// BgLabelControl.idl
namespace BgLabelControlApp
{
runtimeclass BgLabelControl : Microsoft.UI.Xaml.Controls.Control
{
BgLabelControl();
static Microsoft.UI.Xaml.DependencyProperty LabelProperty{ get; };
String Label;
}
}
В приведенном выше списке показан шаблон, которому вы следуете при объявлении свойства зависимости (DP). Для каждого DP есть две части. Во-первых, вы объявляете статическое свойство типа DependencyProperty только для чтения. Оно имеет имя вашего DP плюс Property. Это статическое свойство будет использоваться в реализации. Во-вторых, вы объявляете свойство экземпляра для чтения и записи с типом и именем вашего DP. Если вы хотите создать присоединенное свойство (а не DP), просмотрите примеры кода в настраиваемых присоединенных свойствах.
Note
Если требуется DP с типом с плавающей запятой, сделайте его double (Double в MIDL 3.0). Объявление и реализация DP типа float (Single в MIDL), а затем задание значения для этого DP в разметке XAML приводит к ошибке Не удалось создать объект типа "Windows.Foundation.Single" из текста "<NUMBER>".
Сохраните файл. Сейчас проект не удастся полностью собрать, но запустить сборку сейчас полезно, потому что при этом будут сгенерированы файлы исходного кода, в которых вы реализуете класс времени выполнения BgLabelControl. Итак, теперь выполните сборку (ошибки сборки, которые на этом этапе вполне ожидаемы, будут связаны с «неразрешённым внешним символом»).
Во время процесса midl.exe сборки средство запускается для создания файла метаданных среда выполнения Windows (\BgLabelControlApp\Debug\BgLabelControlApp\Unmerged\BgLabelControl.winmd), описывающего класс среды выполнения. Затем запускается средство cppwinrt.exe для создания файлов исходного кода, чтобы помочь вам при создании и использовании класса среды выполнения. Эти файлы содержат заготовки, которые помогут вам приступить к реализации класса времени выполнения BgLabelControl, который вы объявили в IDL. Эти заглушки — \BgLabelControlApp\BgLabelControlApp\Generated Files\sources\BgLabelControl.h и BgLabelControl.cpp.
Скопируйте файлы-заглушки BgLabelControl.h и BgLabelControl.cpp из \BgLabelControlApp\BgLabelControlApp\Generated Files\sources\ в папку проекта \BgLabelControlApp\BgLabelControlApp\. В Обозреватель решений убедитесь, что включен переключатель "Показать все файлы". Щелкните правой кнопкой мыши по скопированным файлам заглушек, затем выберите Include In Project.
Вы увидите static_assert в верхней части BgLabelControl.h и BgLabelControl.cpp, что вам потребуется удалить. Теперь проект будет построен.
Реализация пользовательского класса элемента управления BgLabelControl
Теперь давайте открываем \BgLabelControlApp\BgLabelControlApp\BgLabelControl.h и BgLabelControl.cpp реализуем класс среды выполнения. В BgLabelControl.h измените конструктор, чтобы задать ключ стиля по умолчанию, реализуйте Label и LabelProperty, добавьте статический обработчик событий с именем OnLabelChanged для обработки изменений значения свойства зависимости и добавьте закрытое поле для хранения значения LabelProperty.
После добавления этих элементов ваш BgLabelControl.h выглядит так. Вы можете скопировать и вставить этот список кода, чтобы заменить содержимое BgLabelControl.h.
// BgLabelControl.h
#pragma once
#include "BgLabelControl.g.h"
namespace winrt::BgLabelControlApp::implementation
{
struct BgLabelControl : BgLabelControlT<BgLabelControl>
{
BgLabelControl() { DefaultStyleKey(winrt::box_value(L"BgLabelControlApp.BgLabelControl")); }
winrt::hstring Label()
{
return winrt::unbox_value<winrt::hstring>(GetValue(m_labelProperty));
}
void Label(winrt::hstring const& value)
{
SetValue(m_labelProperty, winrt::box_value(value));
}
static Microsoft::UI::Xaml::DependencyProperty LabelProperty() { return m_labelProperty; }
static void OnLabelChanged(Microsoft::UI::Xaml::DependencyObject const&, Microsoft::UI::Xaml::DependencyPropertyChangedEventArgs const&);
private:
static Microsoft::UI::Xaml::DependencyProperty m_labelProperty;
};
}
namespace winrt::BgLabelControlApp::factory_implementation
{
struct BgLabelControl : BgLabelControlT<BgLabelControl, implementation::BgLabelControl>
{
};
}
В BgLabelControl.cpp определите статические члены следующим образом. Вы можете скопировать и вставить этот список кода, чтобы заменить содержимое BgLabelControl.cpp.
// BgLabelControl.cpp
#include "pch.h"
#include "BgLabelControl.h"
#include "BgLabelControl.g.cpp"
namespace winrt::BgLabelControlApp::implementation
{
Microsoft::UI::Xaml::DependencyProperty BgLabelControl::m_labelProperty =
Microsoft::UI::Xaml::DependencyProperty::Register(
L"Label",
winrt::xaml_typename<winrt::hstring>(),
winrt::xaml_typename<BgLabelControlApp::BgLabelControl>(),
Microsoft::UI::Xaml::PropertyMetadata{ winrt::box_value(L"default label"), Microsoft::UI::Xaml::PropertyChangedCallback{ &BgLabelControl::OnLabelChanged } }
);
void BgLabelControl::OnLabelChanged(Microsoft::UI::Xaml::DependencyObject const& d, Microsoft::UI::Xaml::DependencyPropertyChangedEventArgs const& /* e */)
{
if (BgLabelControlApp::BgLabelControl theControl{ d.try_as<BgLabelControlApp::BgLabelControl>() })
{
// Call members of the projected type via theControl.
BgLabelControlApp::implementation::BgLabelControl* ptr{ winrt::get_self<BgLabelControlApp::implementation::BgLabelControl>(theControl) };
// Call members of the implementation type via ptr.
}
}
}
В этом пошаговом руководстве мы не будем использовать OnLabelChanged. Но оно нужно для того, чтобы показать, как зарегистрировать свойство зависимости с помощью обратного вызова при изменении свойства. Реализация OnLabelChanged также показывает, как получить производный проектируемый тип из базового прогнозируемого типа (базовый проектируемый тип — DependencyObject, в данном случае). И здесь показано, как затем получить указатель на тип, реализующий проецируемый тип. Эта вторая операция, естественно, будет возможна только в проекте, реализующего проецируемый тип (т. е. проект, реализующий класс среды выполнения).
Note
Если у вас не установлен пакет SDK для Windows версии 10.0.17763.0 (Windows 10 версии 1809) или более поздней версии, то в приведённом выше обработчике события изменения свойства зависимости необходимо вызвать winrt::from_abi вместо winrt::get_self.
Проектирование стиля по умолчанию для BgLabelControl
В конструкторе BgLabelControl задает для себя ключ стиля по умолчанию. Но что такое стиль по умолчанию? Пользовательский шаблонизируемый элемент управления должен иметь стиль по умолчанию — содержащий шаблон элемента управления по умолчанию — который он может использовать для собственного отображения, если пользователь этого элемента управления не задаёт стиль и/или шаблон. В этом разделе мы добавим файл разметки в проект, содержащий наш стиль по умолчанию.
Убедитесь, что Show All Files по-прежнему включен (в Обозреватель решений). В узле проекта создайте новую папку (не фильтр, а папку) и назовите ее "Темы". В разделе Themes, добавьте новый элемент типа Visual C++>XAML>Представление XAML и присвойте ему имя "Generic.xaml". Имена папок и файлов должны быть такими, чтобы платформа XAML могла найти стиль по умолчанию для пользовательского элемента управления. Удалите содержимое по умолчанию из Generic.xaml и вставьте приведённую ниже разметку.
<!-- \Themes\Generic.xaml -->
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:BgLabelControlApp">
<Style TargetType="local:BgLabelControl" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:BgLabelControl">
<Grid Width="100" Height="100" Background="{TemplateBinding Background}">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="{TemplateBinding Label}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
В этом случае единственным свойством, которое задает стиль по умолчанию, является шаблон элемента управления. Шаблон состоит из квадрата (фон которого привязан к свойству Background , которое имеет все экземпляры типа элемента управления XAML), а также текстовый элемент (текст которого привязан к свойству зависимостей BgLabelControl::Label ).
Добавление экземпляра BgLabelControl на главную страницу пользовательского интерфейса
Откройте файл MainPage.xaml, который содержит разметку XAML для главной страницы пользовательского интерфейса. Сразу после элемента Кнопка (внутри StackPanel), добавьте следующую разметку.
<local:BgLabelControl Background="Red" Label="Hello, World!"/>
Кроме того, добавьте следующую директиву include в MainPage.h, чтобы тип MainPage (сочетающий скомпилированную разметку XAML и императивный код) распознавал тип пользовательского элемента управления BgLabelControl. Если вы хотите использовать BgLabelControl с другой страницы XAML, добавьте эту же директиву include в файл заголовка для этой страницы. Или, кроме того, просто поместите одну директиву include в предварительно скомпилированный файл заголовков.
// MainPage.h
...
#include "BgLabelControl.h"
...
Теперь создайте и запустите проект. Вы увидите, что шаблон элемента управления по умолчанию привязан к фоновой кисти и метке в разметке для экземпляра BgLabelControl.
В этом пошаговом руководстве показан простой пример пользовательского (шаблонного) элемента управления в C++/WinRT. Вы можете сделать собственные пользовательские элементы управления произвольными и полнофункциональные. Например, пользовательский элемент управления может принимать форму такого сложного, как редактируемая сетка данных, видеопроигрыватель или визуализатор трехмерной геометрии.
Реализация переопределиваемых методов, таких как MeasureOverride и OnApplyTemplate
См. раздел "Вызов и переопределение базового типа с помощью C++/WinRT".
Важные API
Связанные темы
Windows developer