Hospedaje de un control XAML personalizado de WinRT en una aplicación de WPF mediante islas XAML

Importante

En este tema se usan o mencionan tipos del repositorio de GitHub CommunityToolkit/Microsoft.Toolkit.Win32. Para obtener información importante sobre la compatibilidad con islas XAML, consulte el Aviso de islas XAML en ese repositorio.

En este artículo se muestra cómo usar el control WindowsXamlHost en el kit de herramientas de la comunidad Windows para hospedar un control XAML personalizado de WinRT en una aplicación de WPF orientada a .NET Core 3.1. El control personalizado contiene varios controles propios de Windows SDK y enlaza una propiedad de uno de los controles XAML de WinRT a una cadena de la aplicación de WPF. En este artículo también se muestra cómo hospedar un control de la biblioteca WinUI.

Aunque en este artículo se muestra cómo hacerlo en una aplicación de WPF, el proceso es similar para una aplicación de Windows Forms. Para obtener información general sobre el hospedaje de controles XAML de WinRT en WPF y aplicaciones de Windows Forms, consulta este artículo.

Nota

El uso de islas XAML para hospedar controles XAML de WinRT en las aplicaciones de WPF y Windows Forms que tienen como destino .NET Core 3.x. Las islas XAML todavía no se admiten en aplicaciones destinadas a .NET ni en aplicaciones de cualquier versión de .NET Framework.

Componentes necesarios

Para hospedar un control XAML personalizado de WinRT en una aplicación de WPF (o Windows Forms), necesitará los siguientes componentes en la solución. En este artículo se proporcionan instrucciones para crear cada uno de estos componentes.

  • El proyecto y el código fuente de la aplicación. El uso del control WindowsXamlHost para hospedar controles personalizados solo se admite en las aplicaciones orientadas a .NET Core 3.x.

  • El control XAML personalizado de WinRT. Necesitará el código fuente para el control personalizado que quiere hospedar a fin de poder compilarlo con la aplicación. Normalmente, el control personalizado se define en un proyecto de biblioteca de clases de UWP al que se hace referencia en la misma solución que el proyecto de WPF o Windows Forms.

  • Un proyecto de aplicación para UWP que define una clase de aplicación raíz que se deriva de XamlApplication. El proyecto de WPF o Windows Forms debe tener acceso a una instancia de la clase Microsoft.Toolkit.Win32.UI.XamlHost.XamlApplication proporcionada por el kit de herramientas de la comunidad de Windows, a fin de que pueda detectar y cargar los controles XAML de UWP personalizados. La manera recomendada de hacerlo es definir este objeto en un proyecto de aplicación para UWP independiente que forme parte de la solución para la aplicación de WPF o Windows Forms.

    Nota

    La solución puede contener solo un proyecto que define un objeto XamlApplication. Todos los controles XAML personalizados de WinRT de la aplicación comparten el mismo objeto XamlApplication. El proyecto que define el objeto XamlApplication debe incluir referencias a todas las demás bibliotecas y proyectos de WinRT que se usan para hospedar controles en las islas XAML.

Creación de un proyecto de WPF

Antes de comenzar, sigue estas instrucciones para crear un proyecto de WPF y configurarlo para hospedar XAML Islands. Si tienes un proyecto de WPF existente, puedes adaptar estos pasos y ejemplos de código para el proyecto.

Nota

Si tiene un proyecto existente orientado a .NET Framework, tendrá que migrarlo a .NET Core 3.1. Para obtener más información, consulta esta serie de blog.

  1. Si todavía no lo ha hecho, instale la versión más reciente del SDK de .NET Core 3.1.

  2. En Visual Studio 2019, crea un proyecto de aplicación de WPF (.NET Core) .

  3. Asegúrate de que las referencias de paquete están habilitadas:

    1. En Visual Studio, haga clic en Herramientas-> Administrador de paquetes NuGet-> Configuración del Administrador de paquetes.
    2. Asegúrate de que PackageReference está seleccionado para Formato predeterminado de administración de paquetes.
  4. Haz clic con el botón derecho en el proyecto de WPF en el Explorador de soluciones y elige Administrar paquetes NuGet.

  5. Seleccione la pestaña Examinar, busque el paquete Microsoft.Toolkit.Wpf.UI.XamlHost e instale la versión estable más reciente. Este paquete proporciona todo lo que necesita para usar el control WindowsXamlHost para hospedar un control XAML de WinRT, incluidos otros paquetes de NuGet relacionados.

    Nota

    Las aplicaciones de Windows Forms deben usar el paquete Microsoft.Toolkit.Forms.UI.XamlHost.

  6. Configura la solución para que tenga como destino una plataforma específica, como x86 o x64. Los controles XAML personalizados de WinRT no se admiten en los proyectos que tienen como destino Cualquier CPU.

    1. En el Explorador de soluciones, haga clic con el botón derecho en el nodo de la solución y seleccione Propiedades ->Propiedades de configuración ->Administrador de configuración.
    2. En Plataforma de soluciones activas, selecciona Nueva.
    3. En el cuadro de diálogo Nueva plataforma de solución, selecciona x64 o x86 y, después, presiona Aceptar.
    4. Cierra los cuadros de diálogo abiertos.

Definición de una clase XamlApplication en un proyecto de aplicación para UWP

A continuación, agrega un proyecto de aplicación para UWP a la solución y revisa la clase App predeterminada en este proyecto para derivarla de la clase Microsoft.Toolkit.Win32.UI.XamlHost.XamlApplication proporcionada por el kit de herramientas de la comunidad de Windows. Esta clase admite la interfaz IXamlMetadataProvider, que permite que la aplicación detecte y cargue metadatos para controles XAML de UWP personalizados en ensamblados del directorio actual de la aplicación en tiempo de ejecución. Esta clase también inicializa el marco XAML de UWP para el subproceso actual.

  1. En el Explorador de soluciones, haga clic con el botón derecho en el nodo de la solución y seleccione Agregar ->Nuevo proyecto.

  2. Agrega un proyecto Aplicación vacía (Windows universal) a la solución. Asegúrese de que tanto la versión de destino como la versión mínima estén configuradas en Windows 10, versión 1903 (compilación 18362) o posterior.

  3. En el proyecto de aplicación para UWP, instale el paquete NuGet Microsoft.Toolkit.Win32.UI.XamlApplication (versión estable más reciente).

  4. Abre el archivo App.xaml y reemplaza su contenido por el código XAML siguiente. Reemplaza MyUWPApp por el espacio de nombres del proyecto de aplicación para UWP.

    <xaml:XamlApplication
        x:Class="MyUWPApp.App"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:xaml="using:Microsoft.Toolkit.Win32.UI.XamlHost"
        xmlns:local="using:MyUWPApp">
    </xaml:XamlApplication>
    
  5. Abre el archivo App.xaml.cs y reemplaza el contenido de este archivo por el código siguiente. Reemplaza MyUWPApp por el espacio de nombres del proyecto de aplicación para UWP.

    namespace MyUWPApp
    {
        public sealed partial class App : Microsoft.Toolkit.Win32.UI.XamlHost.XamlApplication
        {
            public App()
            {
                this.Initialize();
            }
        }
    }
    
  6. Elimina el archivo MainPage.xaml del proyecto de aplicación para UWP.

  7. Limpia el proyecto de aplicación para UWP y, a continuación, compílalo.

Adición de una referencia al proyecto de UWP en el proyecto de WPF

  1. Especifique la versión del marco compatible en el archivo de proyecto de WPF.

    1. En el Explorador de soluciones, haga doble clic en el nodo del proyecto de WPF para abrir el archivo del proyecto en el editor.

    2. En el primer elemento PropertyGroup, agregue el siguiente elemento secundario. Cambie la parte 19041 del valor según sea necesario para que coincida con el destino y la compilación mínima del sistema operativo del proyecto de UWP.

      <AssetTargetFallback>uap10.0.19041</AssetTargetFallback>
      

      Cuando haya terminado, el elemento PropertyGroup debe ser similar al siguiente.

      <PropertyGroup>
          <OutputType>WinExe</OutputType>
          <TargetFramework>netcoreapp3.1</TargetFramework>
          <UseWPF>true</UseWPF>
          <Platforms>AnyCPU;x64</Platforms>
          <AssetTargetFallback>uap10.0.19041</AssetTargetFallback>
      </PropertyGroup>
      
  2. En el Explorador de soluciones, haga clic con el botón derecho en el nodo Dependencias del proyecto de WPF y agregue una referencia al proyecto de aplicación para UWP.

Creación de una instancia del objeto XamlApplication en el punto de entrada de la aplicación de WPF

A continuación, agrega código al punto de entrada de la aplicación de WPF para crear una instancia de la clase App que acabas de definir en el proyecto de UWP (esta es la clase que ahora deriva de XamlApplication).

  1. En el proyecto de WPF, haga clic con el botón derecho en el nodo del proyecto, seleccione Agregar ->Nuevo elemento y, después, seleccione Clase. Asigna un nombre a la clase Programa y haz clic en Agregar.

  2. Reemplaza la clase Program generada por el código siguiente y guarda el archivo. Reemplaza MyUWPApp por el espacio de nombres del proyecto de aplicación para UWP y reemplaza MyWPFApp por el espacio de nombres del proyecto de aplicación de WPF.

    public class Program
    {
        [System.STAThreadAttribute()]
        public static void Main()
        {
            using (new MyUWPApp.App())
            {
                MyWPFApp.App app = new MyWPFApp.App();
                app.InitializeComponent();
                app.Run();
            }
        }
    }
    
  3. Haz clic con el botón derecho en el nodo del proyecto y selecciona Propiedades.

  4. En la pestaña Aplicación de las propiedades, haz clic en la lista desplegable Objeto de inicio y elige el nombre completo de la clase Program que agregaste en el paso anterior.

    Nota

    De forma predeterminada, los proyectos de WPF definen una función de punto de entrada Main en un archivo de código generado que no está diseñado para modificarse. Este paso cambia el punto de entrada del proyecto al método Main de la nueva clase Program, lo que permite agregar código que se ejecuta lo antes posible en el proceso de inicio de la aplicación.

  5. Guarda los cambios en las propiedades del proyecto.

Creación de un control XAML personalizado de WinRT

Para crear un control XAML personalizado de WinRT en la aplicación de WPF, debe tener el código fuente del control para poder compilarlo con la aplicación. Normalmente, los controles personalizados se definen en un proyecto de biblioteca de clases de UWP para facilitar la portabilidad.

En esta sección, definirá un sencillo control personalizado en un nuevo proyecto de biblioteca de clases. También puede definir el control personalizado en el proyecto de la aplicación para UWP que creó en la sección anterior. Sin embargo, estos pasos lo hacen en un proyecto de biblioteca de clases independiente con fines ilustrativos, ya que, normalmente, los controles personalizados para la portabilidad se implementan así.

Si ya tienes un control personalizado, puedes usarlo en lugar del control que se muestra aquí. Sin embargo, necesitarás, de todos modos, configurar el proyecto que contiene el control, tal y como se muestra en estos pasos.

  1. En el Explorador de soluciones, haga clic con el botón derecho en el nodo de la solución y seleccione Agregar ->Nuevo proyecto.

  2. Agrega un proyecto de Biblioteca de clases (Windows universal) a la solución. Asegúrese de que tanto la versión de destino como la versión mínima estén establecidas en el mismo destino y compilación del sistema operativo mínima que el proyecto de UWP.

  3. Haz clic con el botón derecho en el archivo de proyecto y selecciona Descargar el proyecto. Vuelve a hacer clic con el botón derecho en el archivo del proyecto y selecciona Editar.

  4. Antes del elemento </Project> de cierre, agrega el siguiente código XML para deshabilitar varias propiedades y, a continuación, guarda el archivo del proyecto. Estas propiedades deben estar habilitadas para hospedar el control personalizado en una aplicación de WPF (o Windows Forms).

    <PropertyGroup>
      <EnableTypeInfoReflection>false</EnableTypeInfoReflection>
      <EnableXBindDiagnostics>false</EnableXBindDiagnostics>
    </PropertyGroup>
    
  5. Haz clic con el botón derecho en el archivo de proyecto y selecciona Volver a cargar el proyecto.

  6. Elimina el archivo Class1.cs predeterminado y agrega un nuevo elemento de Control de usuarioal proyecto.

  7. En el archivo XAML del control de usuario, agrega el elemento StackPanel como elemento secundario del objeto Grid predeterminado. En este ejemplo, se agrega un control TextBlock y, a continuación, se enlaza el atributo Text de ese control al campo XamlIslandMessage.

    <StackPanel Background="LightCoral">
        <TextBlock>This is a simple custom WinRT XAML control</TextBlock>
        <Rectangle Fill="Blue" Height="100" Width="100"/>
        <TextBlock Text="{x:Bind XamlIslandMessage}" FontSize="50"></TextBlock>
    </StackPanel>
    
  8. En el archivo de código subyacente del control de usuario, agrega el campo XamlIslandMessage a la clase de control de usuario, como se muestra a continuación.

    public sealed partial class MyUserControl : UserControl
    {
        public string XamlIslandMessage { get; set; }
    
        public MyUserControl()
        {
            this.InitializeComponent();
        }
    }
    
  9. Compila el proyecto de biblioteca de clases de UWP.

  10. En el proyecto de WPF, haz clic con el botón derecho en el nodo Dependencias y agrega una referencia al proyecto de la biblioteca de clases de UWP.

  11. En el proyecto de la aplicación para WPF que has configurado antes, haz clic con el botón derecho en el nodo Referencias y agrega una referencia al proyecto de la biblioteca de clases de UWP.

  12. Vuelve a compilar toda la solución y asegúrate de que los proyectos se compilen correctamente.

Hospedaje del control XAML personalizado de WinRT en la aplicación de WPF

  1. En el Explorador de soluciones, expande el proyecto de WPF y abre el archivoMainWindow.xaml o cualquier otra ventana en la que quieras hospedar el control personalizado.

  2. En el archivo XAML, agrega la siguiente declaración de espacio de nombres al elemento <Window>.

    xmlns:xaml="clr-namespace:Microsoft.Toolkit.Wpf.UI.XamlHost;assembly=Microsoft.Toolkit.Wpf.UI.XamlHost"
    
  3. En el mismo archivo, agrega el siguiente control al elemento <Grid>. Cambia el atributo InitialTypeName por el nombre completo del control de usuario en el proyecto de la biblioteca de clases de UWP.

    <xaml:WindowsXamlHost InitialTypeName="UWPClassLibrary.MyUserControl" ChildChanged="WindowsXamlHost_ChildChanged" />
    
  4. Abre el archivo de código subyacente y agrega el siguiente código a la clase Window. Este código define un controlador de eventos ChildChanged que asigna el valor del campo XamlIslandMessage del control personalizado de UWP al valor del campo WPFMessage en la aplicación de WPF. Cambia UWPClassLibrary.MyUserControl por el nombre completo del control de usuario en el proyecto de la biblioteca de clases de UWP.

    private void WindowsXamlHost_ChildChanged(object sender, EventArgs e)
    {
        // Hook up x:Bind source.
        global::Microsoft.Toolkit.Wpf.UI.XamlHost.WindowsXamlHost windowsXamlHost =
            sender as global::Microsoft.Toolkit.Wpf.UI.XamlHost.WindowsXamlHost;
        global::UWPClassLibrary.MyUserControl userControl =
            windowsXamlHost.GetUwpInternalObject() as global::UWPClassLibrary.MyUserControl;
    
        if (userControl != null)
        {
            userControl.XamlIslandMessage = this.WPFMessage;
        }
    }
    
    public string WPFMessage
    {
        get
        {
            return "Binding from WPF to UWP XAML";
        }
    }
    
  5. Compila y ejecuta la aplicación y confirma que el control de usuario de UWP se muestra como se esperaba.

Adición de un control de la biblioteca WinUI 2 al control personalizado

Tradicionalmente, los controles XAML de WinRT se han publicado como parte del sistema operativo Windows y están disponibles para los desarrolladores desde Windows SDK. La biblioteca WinUI es un enfoque alternativo en el que las versiones actualizadas de los controles XAML de WinRT de Windows SDK se distribuyen en un paquete de NuGet que no está asociado con las versiones de Windows SDK. Esta biblioteca también incluye nuevos controles que no forman parte de Windows SDK y la plataforma UWP predeterminada.

En esta sección se muestra cómo agregar un control XAML de WinRT desde la biblioteca WinUI 2 al control de usuario.

Nota

Actualmente, las islas XAML solo admiten hospedar controles desde la biblioteca WinUI 2. La compatibilidad con el hospedaje de controles desde la biblioteca WinUI 3 estará disponible en una versión posterior.

  1. En el proyecto de la aplicación para UWP, instala la versión preliminar o la versión de lanzamiento más reciente del paquete NuGet Microsoft.UI.Xaml.

    Nota

    Si la aplicación de escritorio está empaquetada en un paquete MSIX, puedes usar una versión preliminar o de lanzamiento del paquete NugGet Microsoft.UI.Xaml. Si la aplicación de escritorio no está empaquetada con MSIX, debes instalar una versión preliminar del paquete NuGet Microsoft.UI.Xaml.

  2. En el archivo App.xaml de este proyecto, agrega el siguiente elemento secundario al elemento <xaml:XamlApplication>.

    <Application.Resources>
        <XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
    </Application.Resources>
    

    Después de agregar este elemento, el contenido de este archivo debe tener un aspecto similar al siguiente.

    <xaml:XamlApplication
        x:Class="MyUWPApp.App"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:xaml="using:Microsoft.Toolkit.Win32.UI.XamlHost"
        xmlns:local="using:MyUWPApp">
        <Application.Resources>
            <XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
        </Application.Resources>
    </xaml:XamlApplication>
    
  3. En el proyecto de biblioteca de clases de UWP, instala la versión más reciente del paquete de NuGet Microsoft.UI.Xaml (la misma versión que instalaste en el proyecto de la aplicación para UWP).

  4. En el mismo proyecto, abre el archivo XAML para el control de usuario y agrega la siguiente declaración de espacio de nombres al elemento <UserControl>.

    xmlns:winui="using:Microsoft.UI.Xaml.Controls"
    
  5. En el mismo archivo, agrega un elemento <winui:RatingControl /> como secundario de <StackPanel>. Este elemento agrega una instancia de la clase RatingControl de la biblioteca WinUI. Después de agregar este elemento, <StackPanel> debería tener un aspecto similar al siguiente.

    <StackPanel Background="LightCoral">
        <TextBlock>This is a simple custom WinRT XAML control</TextBlock>
        <Rectangle Fill="Blue" Height="100" Width="100"/>
        <TextBlock Text="{x:Bind XamlIslandMessage}" FontSize="50"></TextBlock>
        <winui:RatingControl />
    </StackPanel>
    
  6. Compila y ejecuta la aplicación y confirma que el nuevo control de clasificación se muestra como se esperaba.

Empaquetado de la aplicación

También puedes empaquetar la aplicación de WPF en un paquete MSIX para implementarla. MSIX es una tecnología de empaquetado de aplicaciones moderna para Windows y está basada en una combinación de las tecnologías de instalación MSI, .appx, App-V y ClickOnce.

Las instrucciones siguientes muestran cómo empaquetar todos los componentes de la solución en un paquete MSIX mediante el Proyecto de paquete de aplicación de Windows en Visual Studio 2019. Estos pasos solo son necesarios si quieres empaquetar la aplicación de WPF en un paquete MSIX.

Nota

Si decide no empaquetar la aplicación en un paquete MSIX para su implementación, los equipos que ejecuten esa aplicación deberán tener instalado el entorno de ejecución de Visual C++.

  1. Agrega un nuevo Proyecto de paquete de aplicación de Windows a la solución. Cuando cree el proyecto, seleccione las mismas Versión de destino y Versión mínima que seleccionó para el proyecto de UWP.

  2. En el proyecto de empaquetado, haz clic con el botón derecho en el nodo Aplicaciones y elige Agregar referencia. En la lista de proyectos, selecciona el proyecto de WPF en la solución y haz clic en Aceptar.

    Nota

    Si quiere publicar la aplicación en Microsoft Store, tiene que agregar una referencia al proyecto de UWP en el proyecto de empaquetado.

  3. Configura la solución para que tenga como destino una plataforma específica, como x86 o x64. Esto es necesario para compilar la aplicación de WPF en un paquete MSIX mediante el proyecto de paquete de aplicación de Windows.

    1. En el Explorador de soluciones, haga clic con el botón derecho en el nodo de la solución y seleccione Propiedades ->Propiedades de configuración ->Administrador de configuración.
    2. En Plataforma de soluciones activas, selecciona x64 o x86.
    3. En la fila del proyecto de WPF, en la columna Plataforma, selecciona Nueva.
    4. En el cuadro de diálogo Nueva plataforma de solución, selecciona x64 o x86 (la misma plataforma seleccionada para Plataforma de soluciones activas) y haz clic en Aceptar.
    5. Cierra los cuadros de diálogo abiertos.
  4. Compila y ejecuta el proyecto de empaquetado. Confirma que se ejecuta WPF y que el control personalizado de UWP se muestra como se esperaba.

  5. Para más información sobre cómo distribuir o implementar el paquete, consulte Administración de la implementación MSIX.

Resolución del error "No se encuentra un recurso" al hospedar un control WinUI

Si hospeda un control personalizado que contiene un control de la biblioteca WinUI, puede experimentar un problema en que el control no se puede cargar en una aplicación empaquetada y la depuración del código muestra el siguiente error.

Failed to host WinUI library control

Para resolver este error, copie el archivo App.xbf de la carpeta de salida de compilación del proyecto WPF a la carpeta de salida de compilación \AppX<proyecto de WPF> del proyecto de empaquetado.

Por ejemplo, si el proyecto de WPF se denomina WPFXamlIslandsApp y tiene como destino la plataforma x86, copie App.xbf de \WPFXamlIslandsApp\bin\x86\Release\netcoreapp3.1 a \WPFXamlIslandsApp.Pack\bin\x86\Release\AppX\WPFXamlIslandsAPP.