Uso de islas XAML para hospedar un control XAML para UWP en una aplicación de WPF en C#

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 tema se muestra cómo compilar una aplicación de Windows Presentation Foundation (WPF) en C# (con destino en .NET Core 3.1) que usa islas XAML para hospedar un control XAML para la Plataforma universal de Windows (UWP) (es decir, un control de primera entidad proporcionado por Windows SDK). Se muestran dos maneras de hacerlo:

  • Se muestra cómo hospedar los controles InkCanvas e InkToolbar de UWP con controles encapsulados (disponibles en el kit de herramientas de la comunidad de Windows). Estos controles encapsulan la interfaz y la funcionalidad de un pequeño conjunto de controles XAML para UWP. Puede agregar un control encapsulado directamente en la superficie de diseño del proyecto de WPF o Windows Forms y, luego, usarlo en el diseñador, como cualquier otro control de WPF o Windows Forms.

  • También se muestra cómo hospedar un control CalendarView de UWP mediante el control WindowsXamlHost (disponible en el kit de herramientas de la comunidad de Windows). Dado que solo hay un pequeño conjunto de controles para UWP disponibles como controles encapsulados, puede usar WindowsXamlHost para hospedar cualquier control XAML para UWP.

El proceso para hospedar un control XAML para UWP en una aplicación de WPF es similar para una aplicación de Windows Forms.

Importante

El uso de islas XAML (controles encapsulado o WindowsXamlHost) para hospedar controles XAML para UWP solo se admite en aplicaciones cuyo destino sea .NET Core 3.x. Las islas XAML no se admiten en aplicaciones destinadas a .NET ni en aplicaciones cuyo destino sea cualquier versión de .NET Framework.

Para hospedar un control XAML para UWP en una aplicación de WPF o Windows Forms, se recomienda contar con los siguientes componentes en la solución. En este tema se proporcionan instrucciones para crear cada uno de estos componentes:

  • El proyecto y el código fuente de la aplicación de WPF o Windows Forms.

  • Un proyecto de UWP que define una clase de aplicación raíz que se deriva de XamlApplication. La clase Microsoft.Toolkit.Win32.UI.XamlHost.XamlApplication está disponible en el kit de herramientas de la comunidad de Windows. Se recomienda definir la clase Application derivada de XamlApplication en un proyecto de aplicación para UWP independiente que forme parte de la solución de WPF o Windows Forms en Visual Studio.

    Nota

    No es necesario hacer que un objeto derivado de XamlApplication esté disponible para el proyecto de WPF o Windows Forms para hospedar un control XAML para UWP de primera entidad. Pero es necesario para detectar, cargar y hospedar controles XAML personalizados para UWP. Por lo tanto, para admitir toda la gama de escenarios de islas XAML, se recomienda definir siempre un objeto derivado de XamlApplication en toda solución en la que se usen islas XAML.

    Nota

    La solución puede contener solo un proyecto que defina un objeto derivado de XamlApplication. Ese proyecto único debe hacer referencia a cualquier otra biblioteca y proyecto que hospede controles XAML para UWP a través de islas XAML.

Creación de un proyecto de WPF

Siga estas instrucciones para crear un proyecto de WPF y configurarlo para hospedar islas XAML. Si ya tiene un proyecto de WPF, puede adaptar estos pasos y ejemplos de código para su proyecto.

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

  2. En Visual Studio, cree un nuevo proyecto de C# desde una plantilla de proyecto Aplicación WPF. Establezca Nombre del proyecto en MyWPFApp para que no tenga que editar ninguno de los pasos ni el código fuente de este tema. Establezca Framework EN .NET Core 3.1* y haga clic en Crear.

Importante

Asegúrese de no usar la plantilla de proyecto Aplicación WPF (.NET Framework).

Configuración del proyecto de WPF

  1. Estos pasos habilitan las referencias de paquete:

    1. En Visual Studio, haz clic en Herramientas>Administrador de paquetes NuGet>Configuración del Administrador de paquetes.
    2. A la derecha, busque la opción Administración de paquetes>Formato predeterminado de administración de paquetes y establézcala en PackageReference.
  2. Siga estos pasos para instalar el paquete NuGet Microsoft.Toolkit.Wpf.UI.Controls:

    1. Haga clic con el botón derecho en el nodo de proyecto MyWPFApp en el Explorador de soluciones y elija Administrar paquetes de NuGet.

    2. En la pestaña Examinar, escriba o pegue Microsoft.Toolkit. Wpf.UI.Controls en el cuadro de búsqueda. Seleccione la versión estable más reciente y haga clic en Instalar. Ese paquete proporciona todo lo necesario para usar los controles XAML encapsulados para UWP (incluidos los controles InkCanvas, InkToolbar y WindowsXamlHost).

    Nota

    Para una aplicación de Windows Forms, en su lugar haga referencia al paquete Microsoft.Toolkit.Forms.UI.Controls.

  3. La mayoría de los escenarios de islas XAML no se admiten en los proyectos que tienen como destino Cualquier CPU. Por lo tanto, para tener como destino una arquitectura específica (como x86 o x64), haga lo siguiente:

    1. Haga clic con el botón derecho en el nodo de la solución (no en el nodo de proyecto) en el Explorador de soluciones y elija Propiedades.
    2. Seleccione Propiedades de configuración a la izquierda.
    3. Haga clic en el botón Administrador de configuración…
    4. En Plataforma de soluciones activas, selecciona Nueva.
    5. En el cuadro de diálogo Nueva plataforma de solución, seleccione x64 o x86 y, después, presione Aceptar.
    6. Cierra los cuadros de diálogo abiertos.

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

En esta sección, vamos a agregar un proyecto para UWP a la solución y revisar la clase App predeterminada en ese proyecto para derivarla de la clase Microsoft.Toolkit.Win32.UI.XamlHost.XamlApplication proporcionada por el kit de herramientas de la comunidad de Windows. Esa 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. Esa 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. Seleccione la plantilla de proyecto Aplicación en blanco (Universal Windows) de C#. Establezca Nombre del proyecto en MyUWPApp para que no tenga que editar ninguno de los pasos ni el código fuente de este tema. Establezca Versión de destino y Versión mínima en Windows 10, versión 1903 (compilación 18362) o posterior.

    Nota

    Asegúrese de no crear MyUWPApp en una subcarpeta de MyWPFApp. Si lo hace, MyWPFApp intentará compilar el marcado XAML para UWP como si fuera XAML de WPF.

  3. En MyUWPApp,instale el paquete NuGet Microsoft.Toolkit.Win32.UI.XamlApplication (la versión estable más reciente). El proceso para instalar un paquete NuGet se describió en la sección anterior.

  4. En MyUWPApp, abra el archivo App.xaml y reemplace el contenido por el código XAML 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">
    </xaml:XamlApplication>
    
  5. De manera similar, abra App.xaml.cs y reemplace el contenido por el código siguiente:

    namespace MyUWPApp
    {
        public sealed partial class App : Microsoft.Toolkit.Win32.UI.XamlHost.XamlApplication
        {
            public App()
            {
                this.Initialize();
            }
        }
    }
    
  6. Elimine los archivos MainPage.xaml y MainPage.xaml.cs.

  7. Compile el proyecto MyUWPApp.

En MyWPFApp, agregue una referencia al proyecto MyUWPApp.

  1. Especifique la versión del marco compatible en el archivo del proyecto MyWPFApp del modo siguiente:

    1. En el Explorador de soluciones, haga clic en el nodo del proyecto MyWPFApp 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 MyWPFApp.

      <AssetTargetFallback>uap10.0.19041</AssetTargetFallback>
      
  2. En Explorador de soluciones, haga clic con el botón derecho en MyWPFApp>Dependencias, elija Agregar referencia de proyecto… y agregue una referencia al proyecto MyUWPApp.

Creación de una instancia del objeto XamlApplication en el punto de entrada de MyWPFApp

A continuación, agregue código al punto de entrada de MyWPFApp para crear una instancia de la clase App que acaba de definir en MyUWPApp (la clase que se deriva de XamlApplication).

  1. Haga clic con el botón derecho en el nodo del proyecto MyWPFApp, seleccione Agregar>Nuevo elemento… y, después, selecciona Clase. Establezca Nombre en Program.cs y haga clic en Agregar.

  2. Reemplace el contenido de Program.cs por el código XAML siguiente (y, a continuación, guarde el archivo y compile el proyecto MyWPFApp):

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

  4. En Aplicación>General, haga clic en la lista desplegable Objeto de inicio y elija MyWPFApp.Program (que es el nombre completo de la clase Program que acaba de agregar). Si no lo ve, pruebe a cerrar Visual Studio y volver a abrirlo.

    Nota

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

  5. Guarda los cambios en las propiedades del proyecto.

Uso de controles encapsulados para hospedar InkCanvas e InkToolbar

Ahora que ha configurado el proyecto para que use islas XAML para UWP, ya está listo para agregar a la aplicación los controles XAML encapsulados para UWP InkCanvas e InkToolbar.

  1. En MyWPFApp, abra el archivo MainWindow.xaml.

  2. En el elemento Ventana situado cerca de la parte superior del archivo XAML, agrega el atributo siguiente. Este atributo hace referencia al espacio de nombres XAML para los controles XAML encapsulado para UWP InkCanvas e InkToolbar, y lo asigna al espacio de nombres XML controls.

    xmlns:controls="clr-namespace:Microsoft.Toolkit.Wpf.UI.Controls;assembly=Microsoft.Toolkit.Wpf.UI.Controls"
    
  3. Todavía en MainWindow.xaml, edite el elemento Grid existente para que se parezca al código XAML siguiente. Este código XAML agrega a Grid un control InkCanvas y un control InkToolbar (precedidos por el espacio de nombres XML controls que se definió en el paso anterior).

    <Grid Margin="10,50,10,10">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <controls:InkToolbar x:Name="myInkToolbar" TargetInkCanvas="{x:Reference myInkCanvas}" Grid.Row="0" Width="300"
            Height="50" Margin="10,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top" />
        <controls:InkCanvas x:Name="myInkCanvas" Grid.Row="1" HorizontalAlignment="Left" Width="600" Height="400"
            Margin="10,10,10,10" VerticalAlignment="Top" />
    </Grid>
    

    Nota

    Además, para agregar estos y otros controles encapsulados a Window, puede arrastrarlos al diseñador desde la sección Kit de herramientas de la comunidad de Windows del Cuadro de herramientas.

  4. Guarde MainWindow.xaml.

    Si tiene un dispositivo que admite un lápiz digital, (como Surface) y sigue este artículo en una máquina física, ahora puede compilar y ejecutar la aplicación, y dibujar la entrada de lápiz digital en la pantalla con el lápiz. Pero si intenta escribir con el mouse, no ocurrirá nada, ya que, de manera predeterminada, InkCanvas solo está habilitado para lápices digitales. Aquí se muestra cómo habilitar InkCanvas para el mouse.

  5. Todavía en MyWPFApp, abra MainWindow.xaml.cs.

  6. Agregue la siguiente directiva de espacio de nombre a la parte superior del archivo:

    using Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT;
    
  7. Busque el constructor MainWindow. Inmediatamente después de llamar a InitializeComponent, agregue la siguiente línea de código:

    myInkCanvas.InkPresenter.InputDeviceTypes = CoreInputDeviceTypes.Mouse | CoreInputDeviceTypes.Pen;
    

    Puedes usar el objeto InkPresenter para personalizar la experiencia de entrada manuscrita predeterminada. El código anterior usa la propiedad InputDeviceTypes para habilitar el mouse, así como la entrada manuscrita.

  8. Guarde, compile y ejecute. Si usa un equipo con mouse, confirme que puede dibujar algo en el espacio del lienzo de entrada de lápiz con el mouse.

Hospedaje de un control CalendarView mediante el control host

En esta sección, usaremos el control WindowsXamlHost para agregar un control CalendarView a la aplicación.

Nota

El control WindowsXamlHost se proporciona mediante el paquete Microsoft.Toolkit.Wpf.UI.XamlHost. Ese paquete se incluye en el paquete Microsoft.Toolkit.Wpf.UI.Controls que instaló anteriormente.

  1. En el Explorador de soluciones, en MyWPFApp, abra el archivo MainWindow.xaml.

  2. En el elemento Ventana situado cerca de la parte superior del archivo XAML, agrega el atributo siguiente. Este atributo hace referencia al espacio de nombres XAML para el control WindowsXamlHost y lo asigna al espacio de nombres XML xamlhost.

    xmlns:xamlhost="clr-namespace:Microsoft.Toolkit.Wpf.UI.XamlHost;assembly=Microsoft.Toolkit.Wpf.UI.XamlHost"
    
  3. Todavía en MainWindow.xaml, edite el elemento Grid existente para que se parezca al código XAML siguiente. Este código XAML agrega a Grid un control WindowsXamlHost (precedido por el espacio de nombres XML xamlhost que definió en el paso anterior). Para hospedar un control CalendarView de UWP, este código XAML establece la propiedad InitialTypeName como el nombre completo del control. El código XAML también define un controlador de eventos para el evento ChildChanged, que se genera cuando se ha representado el control hospedado.

    <Grid Margin="10,50,10,10">
        <xamlhost:WindowsXamlHost x:Name="myCalendar" InitialTypeName="Windows.UI.Xaml.Controls.CalendarView"
              Margin="10,10,10,10" Width="600" Height="300" ChildChanged="MyCalendar_ChildChanged" />
    </Grid>
    
  4. Guarde MainWindow.xaml y abra MainWindow.xaml.cs.

  5. Elimine esta línea de código, que agregamos en la sección anterior: myInkCanvas.InkPresenter.InputDeviceTypes = CoreInputDeviceTypes.Mouse | CoreInputDeviceTypes.Pen;.

  6. Agregue la siguiente directiva de espacio de nombre a la parte superior del archivo:

    using Microsoft.Toolkit.Wpf.UI.XamlHost;
    
  7. Agregue el siguiente método del controlador de eventos ChildChanged a la clase MainWindow. Cuando se ha representado el control host, este controlador de eventos se ejecuta y crea un controlador de eventos simple para el evento SelectedDatesChanged del control del calendario.

    private void MyCalendar_ChildChanged(object sender, EventArgs e)
    {
        WindowsXamlHost windowsXamlHost = (WindowsXamlHost)sender;
    
        var calendarView =
            (Windows.UI.Xaml.Controls.CalendarView)windowsXamlHost.Child;
    
        if (calendarView != null)
        {
            calendarView.SelectedDatesChanged += (obj, args) =>
            {
                if (args.AddedDates.Count > 0)
                {
                    MessageBox.Show("The user selected a new date: " +
                        args.AddedDates[0].DateTime.ToString());
                }
            };
        }
    }
    
  8. Guarde, compile y ejecute. Confirme que el control de calendario aparezca en la ventana y que se muestre un cuadro de mensaje al seleccionar una fecha.

Empaquetado de la aplicación

También tiene la opción de empaquetar la aplicación de WPF en un paquete MSIX para su implementación. MSIX es la tecnología de empaquetado de aplicaciones moderna y confiable para Windows.

En las instrucciones siguientes se muestra 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 (consulte Configuración de la aplicación de escritorio para el empaquetado MSIX en Visual Studio). 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 la aplicación deberán tener instalado el Entorno en tiempo de ejecución de Visual C++.

  1. Agregue un nuevo proyecto a la solución creada a partir de la plantilla de proyecto del Proyecto de paquete de aplicación de Windows. 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, haga clic con el botón derecho en el nodo Dependencias y elija Agregar referencia de proyecto…. En la lista de proyectos, seleccione MyWPFApp y haga clic en Aceptar.

    Nota

    Si quiere publicar la aplicación en Microsoft Store, también tendrá que agregar una referencia al proyecto para UWP en el proyecto de empaquetado.

  3. Si ha seguido los pasos hasta este punto, todos los proyectos de la solución tendrán como destino la misma plataforma específica (x86 o x64). Eso es necesario para compilar la aplicación de WPF en un paquete MSIX mediante el Proyecto de paquete de aplicación de Windows. Para confirmarlo, puede seguir estos pasos:

    1. Haga clic con el botón derecho en el nodo de la solución (no en el nodo de proyecto) en el Explorador de soluciones y elija Propiedades.
    2. Seleccione Propiedades de configuración a la izquierda.
    3. Haga clic en el botón Administrador de configuración…
    4. Confirme que todos los proyectos enumerados tengan el mismo valor en Plataforma: x86 o x64.
  4. Haga clic con el botón derecho en el nodo del proyecto de empaquetado que acaba de agregar y haga clic en Establecer como proyecto de inicio.

  5. Compila y ejecuta el proyecto de empaquetado. Confirme que la aplicación de WPF está en ejecución y que los controles para UWP se muestran según lo esperado.

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