Ospitare un controllo XAML WinRT personalizzato in un'app WPF usando le isole XAML

Importante

Questo argomento usa o menziona tipi del repository GitHub CommunityToolkit/Microsoft.Toolkit.Win32. Per informazioni importanti sul supporto per le isole XAML, vedere l'avviso sulle isole XAML nel repository citato.

Questo articolo illustra come usare il controllo WindowsXamlHost in Windows Community Toolkit per ospitare un controllo XAML WinRT personalizzato in un'app WPF destinata a .NET Core 3.1. Il controllo personalizzato contiene diversi controlli proprietari forniti da Windows SDK e associa una proprietà in uno dei controlli XAML WinRT a una stringa nell'app WPF. Questo articolo illustra anche come ospitare un controllo della libreria WinUI.

Anche se questo articolo illustra come eseguire questa operazione in un'app WPF, la procedura è simile a quella da adottare per un'app Windows Forms. Per una panoramica sull'hosting di controlli XAML WinRT nelle app WPF e Windows Forms, vedere questo articolo.

Nota

L'uso delle isole XAML per ospitare controlli XAML WinRT è supportato solo nelle app WPF e Windows Forms con destinazione .NET Core 3x. Le isole XAML non sono ancora supportate nelle app destinate a .NET o in quelle destinate a qualsiasi versione di .NET Framework.

Componenti richiesti

Per ospitare un controllo XAML WinRT personalizzato in un'app WPF (o Windows Forms), nella soluzione devono essere inclusi i componenti seguenti. Questo articolo contiene le istruzioni per creare ciascuno di questi componenti.

  • Il progetto e il codice sorgente per l'app. L'uso del controllo WindowsXamlHost per ospitare controlli personalizzati è supportato solo nelle app destinate a .NET Core 3.

  • Il controllo XAML WinRT personalizzato. Sarà necessario avere il codice sorgente del controllo personalizzato che si vuole ospitare per poterlo compilare con l'app. In genere il controllo personalizzato è definito in un progetto di libreria di classi UWP a cui fai riferimento nella stessa soluzione del progetto WPF o Windows Forms.

  • Un progetto di app UWP che definisce una classe Application radice derivata da XamlApplication. Il progetto WPF o Windows Forms deve accedere a un'istanza della classe Microsoft.Toolkit.Win32.UI.XamlHost.XamlApplication fornita dal toolkit della Community Windows, in modo che tale istanza possa individuare e caricare i controlli XAML UWP personalizzati. Il metodo consigliato per eseguire questa operazione consiste nel definire questo oggetto in un progetto di app UWP separato che fa parte della soluzione per l'app WPF o Windows Forms.

    Nota

    La soluzione può contenere un solo progetto che definisce un oggetto XamlApplication. Tutti i controlli XAML WinRT personalizzati nell'app condividono lo stesso oggetto XamlApplication. Il progetto che definisce l'oggetto XamlApplication deve includere riferimenti a tutti gli altri progetti e librerie WinRT usati per ospitare i controlli nell'isola XAML.

Creare un progetto WPF

Prima di iniziare, segui queste istruzioni per creare un progetto WPF e configurarlo in modo da ospitare le isole XAML. Se disponi di un progetto WPF esistente, puoi adattare questi passaggi ed esempi di codice per il tuo progetto.

Nota

Se è disponibile un progetto esistente destinato a .NET Framework, sarà necessario eseguire la migrazione del progetto a .NET Core 3.1. Per altre informazioni, vedi questa serie di post di blog.

  1. Se non si è già fatto, installare la versione più recente di .NET Core 3.1 SDK.

  2. In Visual Studio 2019 crea un nuovo progetto App WPF (.NET Core).

  3. Assicurati che i riferimenti ai pacchetti siano abilitati:

    1. In Visual Studio fare clic su Strumenti -> Gestione pacchetti NuGet -> Impostazioni di Gestione pacchetti.
    2. Verifica che PackageReference sia selezionato per Formato di gestione pacchetti predefinito.
  4. Fai clic con il pulsante destro del mouse sul progetto WPF in Esplora soluzioni e scegli Gestisci pacchetti NuGet.

  5. Selezionare la scheda Sfoglia, cercare il pacchetto Microsoft.Toolkit.Wpf.UI.XamlHost e installare la versione stabile più recente. Questo pacchetto offre tutto il necessario per usare il controllo WindowsXamlHost per ospitare un controllo XAML WinRT, inclusi altri pacchetti NuGet correlati.

    Nota

    Le app Windows Forms devono usare il pacchetto Microsoft.Toolkit.Forms.UI.XamlHost.

  6. Configura la soluzione in modo da destinarla a una piattaforma specifica, ad esempio x86 o x64. I controlli XAML WinRT personalizzati non sono supportati nei progetti destinati a Qualsiasi CPU.

    1. In Esplora soluzioni fare clic con il pulsante destro del mouse sul nodo della soluzione e scegliere Proprietà ->Proprietà di configurazione ->Gestione configurazione.
    2. In Piattaforma soluzione attiva seleziona Nuovo.
    3. Nella finestra di dialogo Nuova piattaforma soluzione seleziona x64 o x86 e premi OK.
    4. Chiudi le finestre di dialogo aperte.

Definire una classe XamlApplication in un progetto di app UWP

A questo punto, aggiungi un progetto di app UWP alla soluzione e aggiorna la classe App predefinita in questo progetto in modo che derivi dalla classe Microsoft.Toolkit.Win32.UI.XamlHost.XamlApplication fornita da Windows Community Toolkit. Questa classe supporta l'interfaccia IXamlMetadataProvider, che consente all'app di individuare e caricare i metadati dei controlli XAML UWP personalizzati negli assembly nella directory corrente dall'applicazione in fase di esecuzione. La classe inizializza anche il framework XAML UWP per il thread corrente.

  1. In Esplora soluzioni fare clic con il pulsante destro del mouse sul nodo della soluzione e scegliere Aggiungi ->Nuovo progetto.

  2. Aggiungi un progetto App vuota (Windows universale) alla soluzione. Verificare che la versione di destinazione e la versione minima siano entrambe impostate su Windows 10, versione 1903 (Build 18362) o successiva.

  3. Nel progetto dell'app UWP installare il pacchetto NuGet Microsoft.Toolkit.Win32.UI.XamlApplication (versione stabile più recente).

  4. Apri il file App.xaml e sostituisci il contenuto del file con il codice XAML seguente. Sostituisci MyUWPApp con lo spazio dei nomi del progetto di app 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. Apri il file App.xaml.cs e sostituisci il contenuto del file con il codice seguente. Sostituisci MyUWPApp con lo spazio dei nomi del progetto di app UWP.

    namespace MyUWPApp
    {
        public sealed partial class App : Microsoft.Toolkit.Win32.UI.XamlHost.XamlApplication
        {
            public App()
            {
                this.Initialize();
            }
        }
    }
    
  6. Elimina il file MainPage.xaml dal progetto di app UWP.

  7. Pulisci il progetto dell'app UWP e quindi compilalo.

Aggiungere un riferimento al progetto UWP nel progetto WPF

  1. Specificare la versione framework compatibile nel file di progetto WPF.

    1. In Esplora soluzioni fare doppio clic sul nodo del progetto WPF per aprire il file di progetto nell'editor.

    2. Nel primo elemento PropertyGroup aggiungere l'elemento figlio seguente. Modificare la parte 19041 del valore in modo che corrisponda alla build del sistema operativo minima e di destinazione del progetto UWP.

      <AssetTargetFallback>uap10.0.19041</AssetTargetFallback>
      

      Al termine, l'elemento PropertyGroup deve essere simile a quello riportato di seguito.

      <PropertyGroup>
          <OutputType>WinExe</OutputType>
          <TargetFramework>netcoreapp3.1</TargetFramework>
          <UseWPF>true</UseWPF>
          <Platforms>AnyCPU;x64</Platforms>
          <AssetTargetFallback>uap10.0.19041</AssetTargetFallback>
      </PropertyGroup>
      
  2. In Esplora soluzioni fare clic con il pulsante destro del mouse sul nodo Dipendenze nel progetto WPF e aggiungere un riferimento al progetto di app UWP.

Creare un'istanza dell'oggetto XamlApplication nel punto di ingresso dell'app WPF

A questo punto, aggiungi il codice al punto di ingresso per l'app WPF per creare un'istanza della classe App appena definita nel progetto UWP (si tratta della classe che ora deriva da XamlApplication).

  1. Nel progetto WPF fare clic con il pulsante destro del mouse sul nodo del progetto, scegliere Aggiungi ->Nuovo elemento e quindi Classe. Assegna il nome Program alla classe e fai clic su Aggiungi.

  2. Sostituisci la classe Program generata con il codice seguente e quindi salva il file. Sostituisci MyUWPApp con lo spazio dei nomi del progetto di app UWP e MyWPFApp con quello del progetto di app 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. Fai clic con il pulsante destro del mouse sul nodo del progetto e scegli Proprietà.

  4. Nella scheda Applicazione delle proprietà fai clic sull'elenco a discesa Oggetto di avvio e scegli il nome completo della classe Program aggiunta nel passaggio precedente.

    Nota

    Per impostazione predefinita, i progetti WPF definiscono una funzione del punto di ingresso Main in un file di codice generato che non deve essere modificato. Con questo passaggio, il punto di ingresso del progetto viene modificato nel metodo Main della nuova classe Program. Ciò consente di aggiungere codice che viene eseguito il prima possibile nel processo di avvio dell'app.

  5. Salva le modifiche apportate alle proprietà del progetto.

Creare un controllo XAML WinRT personalizzato

Per ospitare un controllo XAML WinRT personalizzato nell'app WPF, è necessario avere a disposizione il codice sorgente per il controllo in modo da poterlo compilare con l'app. In genere i controlli personalizzati sono definiti in un progetto di libreria di classi UWP per semplificare la portabilità.

In questa sezione verrà definito un controllo personalizzato semplice in un nuovo progetto di libreria di classi. In alternativa, è possibile definire il controllo personalizzato nel progetto di app UWP creato nella sezione precedente. Nei passaggi seguenti, tuttavia, questa operazione viene eseguita in un progetto di libreria di classi separato a scopo illustrativo, perché i controlli personalizzati per la portabilità vengono in genere implementati in questo modo.

Se disponi già di un controllo personalizzato, puoi usarlo in alternativa al controllo illustrato qui. Dovrai comunque configurare il progetto che contiene il controllo come illustrato in questa procedura.

  1. In Esplora soluzioni fare clic con il pulsante destro del mouse sul nodo della soluzione e scegliere Aggiungi ->Nuovo progetto.

  2. Aggiungi un progetto Libreria di classi (Windows universale) alla soluzione. Verificare che la versione di destinazione e la versione minima siano entrambe impostate sulla stessa build del sistema operativo minima e di destinazione del progetto UWP.

  3. Fai clic con il pulsante destro del mouse sul file di progetto e scegli Scarica progetto. Fai di nuovo clic con il pulsante destro del mouse sul file di progetto e scegli Modifica.

  4. Prima dell'elemento </Project> di chiusura, aggiungi il codice XML seguente per disabilitare alcune proprietà e quindi salva il file di progetto. Queste proprietà devono essere abilitate per ospitare il controllo personalizzato in un'app WPF (o Windows Forms).

    <PropertyGroup>
      <EnableTypeInfoReflection>false</EnableTypeInfoReflection>
      <EnableXBindDiagnostics>false</EnableXBindDiagnostics>
    </PropertyGroup>
    
  5. Fai clic con il pulsante destro del mouse sul file di progetto e scegli Ricarica progetto.

  6. Elimina il file Class1.cs predefinito e aggiungi al progetto un nuovo elemento Controllo utente.

  7. Nel file XAML per il controllo utente aggiungi l'oggetto StackPanel seguente come figlio dell'oggetto Grid predefinito. In questo esempio viene aggiunto un controllo TextBlock e quindi viene associato l'attributo Text di tale controllo 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. Nel file code-behind per il controllo utente aggiungi il campo XamlIslandMessage alla classe del controllo utente, come illustrato di seguito.

    public sealed partial class MyUserControl : UserControl
    {
        public string XamlIslandMessage { get; set; }
    
        public MyUserControl()
        {
            this.InitializeComponent();
        }
    }
    
  9. Compila il progetto di libreria di classi UWP.

  10. Nel progetto WPF fai clic con il pulsante destro del mouse sul nodo Dipendenze e aggiungi un riferimento al progetto di libreria di classi UWP.

  11. Nel progetto dell'app UWP configurato in precedenza, fai clic con il pulsante destro del mouse sul nodo Riferimenti e aggiungi un riferimento al progetto di libreria di classi UWP.

  12. Ricompila l'intera soluzione e verifica che tutti i progetti vengano compilati correttamente.

Ospitare il controllo XAML WinRT personalizzato nell'app WPF

  1. In Esplora soluzioni espandi il progetto WPF e apri il file MainWindow.xaml o un'altra finestra in cui vuoi ospitare il controllo personalizzato.

  2. Nel file XAML aggiungi la dichiarazione di spazio dei nomi seguente all'elemento <Window>.

    xmlns:xaml="clr-namespace:Microsoft.Toolkit.Wpf.UI.XamlHost;assembly=Microsoft.Toolkit.Wpf.UI.XamlHost"
    
  3. Nello stesso file aggiungi il controllo seguente all'elemento <Grid>. Imposta l'attributo InitialTypeName sul nome completo del controllo utente nel progetto di libreria di classi UWP.

    <xaml:WindowsXamlHost InitialTypeName="UWPClassLibrary.MyUserControl" ChildChanged="WindowsXamlHost_ChildChanged" />
    
  4. Apri il file code-behind e aggiungi il codice seguente alla classe Window. Questo codice definisce un gestore eventi ChildChanged che assegna il valore del campo XamlIslandMessage del controllo UWP personalizzato al valore del campo WPFMessage nell'app WPF. Imposta UWPClassLibrary.MyUserControl sul nome completo del controllo utente nel progetto di libreria di classi 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 ed esegui l'app e quindi verifica che il controllo utente UWP venga visualizzato come previsto.

Aggiungere un controllo dalla libreria WinUI 2 al controllo personalizzato

Tradizionalmente, i controlli XAML WinRT sono stati rilasciati come parte del sistema operativo Windows e resi disponibili agli sviluppatori tramite Windows SDK. La libreria WinUI è un approccio alternativo, in cui le versioni aggiornate dei controlli XAML WinRT di Windows SDK vengono distribuite in un pacchetto NuGet non associato a versioni di Windows SDK. Questa libreria include anche nuovi controlli che non fanno parte di Windows SDK e della piattaforma UWP predefinita.

Questa sezione illustra come aggiungere un controllo XAML WinRT della libreria WinUI 2 al controllo utente.

Nota

Le isole XAML supportano attualmente solo l'hosting dei controlli della libreria WinUI 2. Il supporto per l'hosting dei controlli della libreria WinUI 3 sarà disponibile in una versione successiva.

  1. Nel progetto dell'app UWP installa la versione più recente o preliminare del pacchetto NuGet Microsoft.UI.Xaml.

    Nota

    Se l'app desktop è assemblata in un pacchetto MSIX, puoi usare una versione preliminare o definitiva del pacchetto NuGet Microsoft.UI.Xaml. Se l'app desktop non è assemblata in un pacchetto con MSIX, devi installare una versione preliminare del pacchetto NuGet Microsoft.UI.Xaml.

  2. Nel file App.xaml di questo progetto aggiungi l'elemento figlio seguente all'elemento <xaml:XamlApplication>.

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

    Dopo l'aggiunta di questo elemento, il contenuto di questo file dovrebbe avere un aspetto simile al seguente.

    <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. Nel progetto di libreria di classi UWP installa la versione più recente del pacchetto NuGet Microsoft.UI.Xaml (la stessa versione installata nel progetto di app UWP).

  4. Nello stesso progetto apri il file XAML per il controllo utente e aggiungi la dichiarazione di spazio dei nomi seguente all'elemento <UserControl>.

    xmlns:winui="using:Microsoft.UI.Xaml.Controls"
    
  5. Nello stesso file aggiungi un elemento <winui:RatingControl /> come figlio dell'oggetto <StackPanel>. Questo elemento aggiunge un'istanza della classe RatingControl dalla libreria WinUI. Dopo l'aggiunta di questo elemento, l'oggetto <StackPanel> dovrebbe avere un aspetto simile al seguente.

    <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 ed esegui l'applicazione e verifica che il nuovo elemento RatingControl venga visualizzato come previsto.

Creare un pacchetto per l'app

Facoltativamente, puoi creare per l'app WPF un pacchetto MSIX da usare per la distribuzione. MSIX è la moderna tecnologia per la creazione di pacchetti di app per Windows ed è basata su una combinazione delle tecnologie di installazione MSI, .appx, App-V e ClickOnce.

Le istruzioni seguenti illustrano come creare un pacchetto MSIX di tutti i componenti della soluzione usando Progetto di creazione pacchetti per applicazioni Windows in Visual Studio 2019. Questi passaggi sono necessari solo se vuoi creare un pacchetto MSIX per l'app WPF.

Nota

Scegliendo di non creare un pacchetto MSIX dell'applicazione per la distribuzione, nei computer che eseguono l'app deve essere installato il runtime di Visual C++.

  1. Aggiungi un nuovo progetto alla soluzione usando Progetto di creazione pacchetti per applicazioni Windows. Durante la creazione del progetto, per Versione di destinazione e Versione minima selezionare lo stesso valore scelto per il progetto UWP.

  2. Nel progetto di creazione del pacchetto fai clic con il pulsante destro del mouse sul nodo Applicazioni e scegli Aggiungi riferimento. Nell'elenco dei progetti seleziona il progetto WPF nella soluzione e fai clic su OK.

    Nota

    Se si vuole pubblicare l'app in Microsoft Store, è necessario aggiungere un riferimento al progetto UWP nel progetto di creazione pacchetti.

  3. Configura la soluzione in modo da destinarla a una piattaforma specifica, ad esempio x86 o x64. Questa operazione è necessaria per compilare l'app WPF in un pacchetto MSIX usando Progetto di creazione pacchetti per applicazioni Windows.

    1. In Esplora soluzioni fare clic con il pulsante destro del mouse sul nodo della soluzione e scegliere Proprietà ->Proprietà di configurazione ->Gestione configurazione.
    2. In Piattaforma soluzione attiva seleziona x64 o x86.
    3. Nella colonna Piattaforma della riga relativa al progetto WPF seleziona Nuovo.
    4. Nella finestra di dialogo Nuova piattaforma soluzione seleziona x64 o x86 (la stessa piattaforma selezionata per Piattaforma soluzione attiva) e fai clic su OK.
    5. Chiudi le finestre di dialogo aperte.
  4. Compila ed esegui il progetto di creazione del pacchetto. Verifica che WPF venga eseguito e che il controllo UWP personalizzato sia visualizzato come previsto.

  5. Per informazioni sulla distribuzione del pacchetto, vedere Gestire la distribuzione MSIX.

Risolvere l'errore di risorsa non trovata quando si ospita un controllo WinUI

Se si ospitan un controllo personalizzato che contiene un controllo dalla libreria WinUI, può verificarsi un problema per cui il controllo non può essere caricato in un'app in pacchetto e il debug del codice mostra l'errore seguente.

Failed to host WinUI library control

Per risolvere questo errore, copiare il file App.xbf dalla cartella dell'output di compilazione del progetto WPF alla cartella dell'output di compilazione \AppX\<progetto WPF> del progetto di creazione pacchetti.

Ad esempio, se il progetto WPF è denominato WPFXamlIslandsApp e ha come destinazione la piattaforma x86, copiare App.xbf da \WPFXamlIslandsApp\bin\x86\Release\netcoreapp3.1 a \WPFXamlIslandsApp.Pack\bin\x86\Release\AppX\WPFXamlIslandsAPP.