Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Important
Para conocer los conceptos y términos esenciales que admiten su comprensión de cómo consumir y crear clases en tiempo de ejecución con C++/WinRT, consulte Consumo de API con C++/WinRT y Creación de API con C++/WinRT.
Una de las características más eficaces del SDK de Aplicaciones para Windows es la flexibilidad que proporciona la pila de interfaz de usuario (UI) para crear controles personalizados basados en el tipo de control XAML. El marco de interfaz de usuario XAML proporciona características como propiedades de dependencia personalizadas y propiedades adjuntas, y plantillas de control, lo que facilita la creación de controles personalizables y enriquecidos con características. Este tema le guía por los pasos necesarios para crear un control personalizado (con plantilla) con C++/WinRT.
Crear una aplicación en blanco (BgLabelControlApp)
Empiece por crear un nuevo proyecto en Microsoft Visual Studio. Cree una Blank App, Packaged (WinUI 3 in Desktop) para un proyecto de C++, asígnele el nombre BgLabelControlApp y, para que la estructura de carpetas coincida con la de la guía, asegúrese de que Colocar la solución y el proyecto en el mismo directorio esté desmarcada. Tenga como destino la versión más reciente disponible con carácter general (es decir, no en versión preliminar) del SDK de Windows.
Más adelante en este tema, se te indicará que compiles tu proyecto (pero no lo compiles hasta entonces).
Note
Para obtener información sobre cómo configurar Visual Studio para el desarrollo de C++/WinRT, incluida la instalación y el uso de la extensión de Visual Studio de C++/WinRT (VSIX) y el paquete NuGet (que juntos proporcionan compatibilidad con la plantilla de proyecto y la compilación), consulte Visual Studio compatibilidad con C++/WinRT.
Vamos a crear una nueva clase para representar un control personalizado (con plantilla). Estamos creando y consumiendo la clase dentro de la misma unidad de compilación. Pero queremos poder crear instancias de esta clase desde el marcado XAML y, por ese motivo, va a ser una clase en tiempo de ejecución. Y vamos a usar C++/WinRT para crearlo y consumirlo.
El primer paso para crear una nueva clase en tiempo de ejecución es agregar un nuevo elemento Midl File (.idl) al proyecto. Asígnalo BgLabelControl.idl. Elimine el contenido predeterminado de BgLabelControl.idl, y pegue en él esta declaración de clase de tiempo de ejecución.
// BgLabelControl.idl
namespace BgLabelControlApp
{
runtimeclass BgLabelControl : Microsoft.UI.Xaml.Controls.Control
{
BgLabelControl();
static Microsoft.UI.Xaml.DependencyProperty LabelProperty{ get; };
String Label;
}
}
La lista anterior muestra el patrón que sigue al declarar una propiedad de dependencia (DP). Hay dos piezas en cada DP. En primer lugar, declara una propiedad estática de solo lectura de tipo DependencyProperty. Tiene el nombre de su DP plus Property. Usará esta propiedad estática en su implementación. En segundo lugar, declara una propiedad de instancia de lectura y escritura con el tipo y el nombre de tu DP. Si desea crear una propiedad adjunta (en lugar de un DP), vea los ejemplos de código en Propiedades adjuntas personalizadas.
Note
Si desea un DP con un tipo de punto flotante, consértelo double (Double en MIDL 3.0). Declarar e implementar un DP de tipo float (Single en MIDL) y, a continuación, establecer un valor para ese DP en el marcado XAML, da como resultado el error No se pudo crear un "Windows". Foundation.Single" del texto "<NUMBER>".
Guarde el archivo. El proyecto no se compilará hasta su finalización en este momento, pero la compilación ahora es algo útil, ya que genera los archivos de código fuente en los que implementará la clase en tiempo de ejecución BgLabelControl . Por lo tanto, continúe y compile ahora (los errores de compilación que puede esperar ver en esta fase tienen que ver con un "símbolo externo sin resolver").
Durante el proceso de compilación, la midl.exe herramienta se ejecuta para crear un archivo de metadatos de Windows Runtime (\BgLabelControlApp\Debug\BgLabelControlApp\Unmerged\BgLabelControl.winmd) que describa la clase en tiempo de ejecución. A continuación, se ejecuta la herramienta cppwinrt.exe para generar archivos de código fuente que le ayuden a crear y consumir su clase de tiempo de ejecución. Estos archivos incluyen códigos auxiliares para empezar a implementar la clase en tiempo de ejecución BgLabelControl que declaró en su IDL. Esos stubs son \BgLabelControlApp\BgLabelControlApp\Generated Files\sources\BgLabelControl.h y BgLabelControl.cpp.
Copie los archivos stub BgLabelControl.h y BgLabelControl.cpp desde \BgLabelControlApp\BgLabelControlApp\Generated Files\sources\ a la carpeta del proyecto, que es \BgLabelControlApp\BgLabelControlApp\. En Explorador de soluciones, asegúrese de que Mostrar todos los archivos está activado. Haga clic con el botón derecho en los archivos auxiliares que copió y haga clic en Incluir en Project.
Verá un static_assert arriba en BgLabelControl.h y BgLabelControl.cpp, que deberá eliminar. Ahora se compilará el proyecto.
Implementación de la clase de control personalizado BgLabelControl
Ahora, abramos \BgLabelControlApp\BgLabelControlApp\BgLabelControl.h y BgLabelControl.cpp e implementemos nuestra clase de tiempo de ejecución. En BgLabelControl.h, cambie el constructor para establecer la clave de estilo predeterminada, implemente Label y LabelProperty, agregue un controlador de eventos estático denominado OnLabelChanged para procesar los cambios en el valor de la propiedad de dependencia y agregue un miembro privado para almacenar el campo de respaldo para LabelProperty.
Después de agregarlos, BgLabelControl.h tiene este aspecto. Puede copiar y pegar esta lista de código para reemplazar el contenido de 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>
{
};
}
En BgLabelControl.cpp, define los miembros estáticos así. Puede copiar y pegar esta lista de código para reemplazar el contenido de 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.
}
}
}
En este tutorial, no usaremos OnLabelChanged. Pero está ahí para que puedas ver cómo registrar una propiedad de dependencia con una devolución de llamada al cambiar la propiedad. La implementación de OnLabelChanged también muestra cómo obtener un tipo proyectado derivado de un tipo proyectado base (el tipo proyectado base es DependencyObject, en este caso). Esto muestra cómo obtener un puntero al tipo que implementa el tipo proyectado. Esa segunda operación solo será posible naturalmente en el proyecto que implementa el tipo proyectado (es decir, el proyecto que implementa la clase en tiempo de ejecución).
Note
Si no ha instalado la versión 10.0.17763.0 del SDK de Windows (Windows 10, versión 1809) o una versión posterior, debe llamar a winrt::from_abi en el controlador del evento de cambio de la propiedad de dependencia anterior, en lugar de winrt::get_self.
Diseñar el estilo predeterminado para BgLabelControl
En su constructor, BgLabelControl establece una clave de estilo predeterminada para sí misma. ¿Pero qué es un estilo predeterminado? Un control personalizado (con plantilla) debe tener un estilo predeterminado, que contiene una plantilla de control predeterminada, que puede usar para representarse con en caso de que el consumidor del control no establezca un estilo o una plantilla. En esta sección agregaremos un archivo de marcado al proyecto que contiene nuestro estilo predeterminado.
Asegúrese de que Show All Files todavía está activado (en Explorador de soluciones). En el nodo del proyecto, cree una nueva carpeta (no un filtro, sino una carpeta) y asígnela el nombre "Temas". En Themes, agregue un nuevo elemento de tipo Visual C++>XAML>XAML View y asígnele el nombre "Generic.xaml". Los nombres de carpeta y archivo deben ser similares para que el marco XAML encuentre el estilo predeterminado para un control personalizado. Elimine el contenido predeterminado de Generic.xamly pegue el marcado siguiente.
<!-- \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>
En este caso, la única propiedad que establece el estilo predeterminado es la plantilla de control. La plantilla consta de un cuadrado (cuyo fondo está enlazado a la propiedad Background que tienen todas las instancias del tipo control XAML) y un elemento de texto (cuyo texto está enlazado a la propiedad de dependencia BgLabelControl::Label ).
Adición de una instancia de BgLabelControl a la página principal de la interfaz de usuario
Abra MainPage.xaml, que contiene el marcado XAML para nuestra página principal de la interfaz de usuario. Inmediatamente después del elemento Button (dentro de la clase StackPanel), agrega el marcado siguiente.
<local:BgLabelControl Background="Red" Label="Hello, World!"/>
Además, agrega la siguiente directiva de inclusión a MainPage.h para que el tipo MainPage (una combinación de compilación de marcado XAML y código imperativo) reconozca el tipo de control personalizado BgLabelControl. Si quieres usar BgLabelControl desde otra página XAML, agrega también esta misma directiva include al archivo de encabezado de esa página. O bien, como alternativa, simplemente coloque una única directiva include en el archivo de encabezado precompilado.
// MainPage.h
...
#include "BgLabelControl.h"
...
Ahora compile y ejecute el proyecto. Verá que la plantilla de control predeterminada está vinculada al pincel de fondo y a la etiqueta de la instancia BgLabelControl en el marcado.
En este tutorial se muestra un ejemplo sencillo de un control personalizado (con plantilla) en C++/WinRT. Puede crear sus propios controles personalizados tan complejos y con tantas funciones como quiera. Por ejemplo, un control personalizado puede adoptar la forma de algo tan complicado como una cuadrícula de datos editable, un reproductor de vídeo o un visualizador de geometría 3D.
Implementación de métodos invalidables , como MeasureOverride y OnApplyTemplate
Consulte la sección Llamar a su tipo base y reemplazarlo con C++/WinRT.