Condividi tramite


Interoperatività di WPF e Win32

In questo argomento vengono forniti cenni preliminari sull'interoperatività tra codice WPF e Win32. Windows Presentation Foundation (WPF) fornisce un ambiente dettagliato per la creazione di applicazioni. Tuttavia, in caso di un investimento ingente in codice Win32, può essere più efficiente riutilizzare parte di tale codice.

Nel presente argomento sono contenute le seguenti sezioni.

  • Nozioni fondamentali sull'interoperatività di WPF e Win32
  • Progetti di interoperatività WPF
  • Utilizzo di Hwnds in WPF
  • Hosting di contenuto WPF in una finestra Microsoft Win32
  • Hosting di una finestra Microsoft Win32 in WPF
  • Tabulazioni, tasti di scelta e tasti di scelta rapida
  • Argomenti correlati

Nozioni fondamentali sull'interoperatività di WPF e Win32

Sono disponibili due tecniche di base per l'interoperatività tra codice WPF e Win32.

  • Ospitare il contenuto WPF in una finestra Win32. Con questa tecnica, è possibile utilizzare le funzionalità grafiche avanzate di WPF all'interno del framework di una finestra e di un'applicazione Win32 standard.

  • Ospitare una finestra Win32 nel contenuto WPF. Con questa tecnica, è possibile utilizzare un controllo Win32 personalizzato esistente nel contesto di altro contenuto WPF e passare i dati oltre i limiti.

In questo argomento vengono presentate le due tecniche. Per informazioni maggiormente orientate al codice sull'hosting di WPF in Win32, vedere Procedura dettagliata: hosting di contenuto WPF in Win32. Per informazioni maggiormente orientate al codice sull'hosting di Win32 in WPF, vedere Procedura dettagliata: hosting di un controllo Win32 in WPF.

Progetti di interoperatività WPF

Le APIs WPF sono costituite da codice gestito, ma la maggior parte dei programmi Win32 esistenti è scritta in codice C++ non gestito. Non è possibile chiamare APIs WPF da un programma non gestito vero e proprio. Tuttavia, utilizzando l'opzione /clr con il compilatore Microsoft Visual C++, è possibile creare un programma misto gestito e non gestito in cui combinare chiamate ad API gestite e non gestite senza problemi.

Una complicazione a livello di progetto è costituita dal fatto che non è possibile compilare file Extensible Application Markup Language (XAML) in un progetto C++. Per compensare tale problema, sono disponibili diverse tecniche di divisione del progetto.

  • Creare una DLL C# contenente tutte le pagine XAML come assembly compilato, quindi fare sì che il file eseguibile C++ includa tale DLL come riferimento.

  • Creare un eseguibile C# per il contenuto WPF e impostarlo affinché faccia riferimento a una DLL C++ contenente il contenuto Win32.

  • Utilizzare Load per caricare XAML in fase di esecuzione, anziché compilare XAML personalizzato.

  • Non utilizzare affatto XAML e scrivere tutti gli elementi WPF nel codice, costruendo la struttura ad albero dell'elemento da Application.

Utilizzare l'approccio più funzionale per lo scenario in questione.

NotaNota

Se si utilizza C++/CLI per la prima volta, è possibile che si notino alcune nuove parole chiave, quali gcnew e nullptr, negli esempi di codice di interoperatività. Queste parole chiave sostituiscono la sintassi con doppio carattere di sottolineatura (__gc) precedente e forniscono una sintassi più naturale per il codice gestito in C++.Per ulteriori informazioni sulle funzionalità gestite di C++/CLI, vedere Language Features for Targeting the CLR e Hello, C++/CLI (la pagina potrebbe essere in inglese).

Utilizzo di Hwnds in WPF

Per la maggior parte delle operazioni di interoperatività HWND di WPF, è necessario comprendere la modalità di utilizzo di HWND in WPF. Per qualsiasi HWND, non è possibile combinare rendering WPF con rendering DirectX o GDI / GDI+, condizione che ha varie implicazioni. Principalmente, per combinare questi modelli di rendering, è necessario creare una soluzione di interoperatività e utilizzare segmenti designati di interoperatività per ogni modello di rendering che si decide di utilizzare. Inoltre, il comportamento di rendering crea una restrizione di "spazio aereo" per i risultati della soluzione di interoperatività. Il concetto di "spazio aereo" è illustrato più dettagliatamente nell'argomento Cenni preliminari sulle aree di tecnologia.

Tutti gli elementi WPF nella schermata sono supportati da HWND. Quando si crea un oggetto Window WPF, in WPF viene creato HWND di primo livello e viene utilizzato un oggetto HwndSource per inserire Window e il relativo contenuto WPF in HWND. Il resto del contenuto WPF nell'applicazione condivide tale HWND. Un'eccezione è costituita da menu, caselle combinate a discesa e altri popup. Poiché questi elementi creano una propria finestra di primo livello, è possibile che un menu WPF superi potenzialmente il bordo dell'elemento HWND finestra che lo contiene. Quando si utilizza HwndHost per inserire un elemento HWND in WPF, WPF segnala a Win32 come posizionare il nuovo elemento HWND figlio relativo all'elemento HWND Window WPF.

Un concetto correlato a HWND è la trasparenza in ogni elemento HWND e tra ogni elemento HWND Questo concetto viene illustrato nell'argomento Cenni preliminari sulle aree di tecnologia.

Hosting di contenuto WPF in una finestra Microsoft Win32

La chiave dell'hosting di WPF in una finestra Win32 è la classe HwndSource. Questa classe esegue il wrapping del contenuto WPF in una finestra Win32, consentendo di incorporare il contenuto WPF nell'user interface (UI) come finestra figlio. Nell'approccio illustrato di seguito Win32 e WPF sono combinati in una sola applicazione.

  1. Implementare il contenuto WPF (elemento radice di contenuto) come classe gestita. In genere, la classe eredita da una delle classi che può contenere più elementi figlio e/o utilizzata come elemento radice, ad esempio DockPanel o Page. Nei passaggi successivi, questa classe è indicata come classe contenuto WPF e le istanze di tale classe come oggetti contenuto WPF.

  2. Implementare un'applicazione Win32 con C++/CLI. Se si parte da un'applicazione C++ non gestita esistente, è in genere possibile consentire la chiamata a codice gestito modificando le impostazioni del progetto in modo da includere il flag del compilatore /clr (l'ambito completo degli elementi necessari per supportare la compilazione /clr non viene illustrato in questo argomento).

  3. Impostare il modello di threading su apartment a thread singolo (STA). WPF utilizza questo modello di threading.

  4. Gestire la notifica WM_CREATE nella procedura della finestra.

  5. All'interno del gestore (o di una funzione chiamata dal gestore), eseguire le operazioni seguenti:

    1. Creare un nuovo oggetto HwndSource con l'elemento HWND finestra padre come parametro parent.

    2. Creare un'istanza della classe di contenuto WPF.

    3. Assegnare alla proprietà RootVisual dell'oggetto HwndSource un riferimento all'oggetto contenuto WPF.

    4. La proprietà Handle dell'oggetto HwndSource contiene l'handle di finestra (HWND). Per ottenere HWND utilizzabile nella parte non gestita dell'applicazione, eseguire il cast di Handle.ToPointer() a un elemento HWND.

  6. Implementare una classe gestita che contiene un campo statico contenente un riferimento all'oggetto contenuto WPF. Questa classe consente di ottenere un riferimento all'oggetto contenuto WPF dal codice Win32 ma, dato più importante, impedisce di sottoporre inavvertitamente HwndSource a procedure di Garbage Collection.

  7. Ricevere notifiche dall'oggetto contenuto WPF associando un gestore a uno o più eventi dell'oggetto contenuto WPF.

  8. Comunicare con l'oggetto contenuto WPF utilizzando il riferimento archiviato nel campo statico per impostare proprietà, chiamare metodi e così via.

NotaNota

È possibile eseguire parzialmente o completamente la definizione della classe contenuto WPF del primo passaggio in XAML utilizzando la classe parziale predefinita della classe contenuto, se si produce un assembly separato a cui si fa riferimento. Anche se in genere si include un oggetto Application come parte della compilazione di XAML in un assembly, il risultato non comporta l'utilizzo di tale oggettoApplication come parte dell'interoperatività, ma solo l'utilizzo di una o più classi radice per i file XAML a cui l'applicazione fa riferimento e il riferimento alle relative classi parziali.Il resto della procedura è essenzialmente analogo a quanto illustrato in precedenza.

Ciascuno di questi passaggi viene illustrato tramite codice nell'argomento Procedura dettagliata: hosting di contenuto WPF in Win32.

Hosting di una finestra Microsoft Win32 in WPF

La chiave per l'hosting di una finestra Win32 all'interno di altro contenuto WPF è la classe HwndHost. Questa classe esegue il wrapping della finestra in un elemento WPF che può essere aggiunto a una struttura ad albero di elementi WPF. HwndHost supporta anche le APIs che consentono di eseguire tali attività come messaggi del processo per la finestra di hosting. La procedura di base è la seguente:

  1. Creare una struttura ad albero dell'elemento per un'applicazione WPF (tramite codice o markup). Trovare un punto adatto e consentito nella struttura ad albero dell'elemento in cui sia possibile aggiungere l'implementazione di HwndHost come elemento figlio. Nei restanti passaggi, viene fatto riferimento a questo elemento come elemento di riserva.

  2. Creare un oggetto contenente il contenuto Win32 derivando da HwndHost.

  3. Nella classe host, eseguire l'override del metodo HwndHost BuildWindowCore. Restituire l'elemento HWND della finestra di hosting. Può essere necessario eseguire il wrapping dei controlli effettivi come finestra figlio della finestra restituita. Questa operazione offre un modo semplice per fare sì che il contenuto WPF riceva notifiche dai controlli. Questa tecnica consente di correggere alcuni problemi Win32 relativi alla gestione dei messaggi sul limite del controllo ospitato.

  4. Eseguire l'override dei metodi HwndHost DestroyWindowCore e WndProc. Lo scopo è quello di eseguire la pulitura e rimuovere riferimenti al contenuto ospitato, in particolare se sono stati creati riferimenti a oggetti non gestiti.

  5. Nel file code-behind, creare un'istanza della classe di hosting del controllo e renderla l'elemento figlio dell'elemento di riserva. In genere si utilizza un gestore eventi quale Loaded oppure il costruttore di classe parziale. È tuttavia anche possibile aggiungere il contenuto di interoperatività tramite un comportamento in fase di esecuzione.

  6. Elaborare i messaggi finestra selezionati, ad esempio le notifiche dei controlli. Sono disponibili due approcci che forniscono entrambi un identico accesso al flusso di messaggi, pertanto la scelta dipende essenzialmente dalle esigenze di programmazione.

    • Implementare l'elaborazione per tutti i messaggi (non solo quelli di arresto) nell'override del metodo HwndHost WndProc.

    • Fare sì che l'elemento WPF di hosting elabori i messaggi gestendo l'evento MessageHook. Questo evento viene generato per ogni messaggio inviato alla routine della finestra principale della finestra ospitata.

    • Non è possibile elaborare messaggi da finestre esterne al processo utilizzando WndProc.

  7. Comunicare con la finestra ospitata utilizzando platform invoke per chiamare la funzione SendMessage non gestita.

Tramite questi passaggi viene creata un'applicazione che funziona con input del mouse. È possibile aggiungere il supporto della tabulazione per la finestra ospitata implementando l'interfaccia IKeyboardInputSink.

Ognuno di questi passaggi viene illustrato tramite codice nell'argomento Procedura dettagliata: hosting di un controllo Win32 in WPF.

HWND in WPF

L'oggetto HwndHost può essere considerato come un controllo speciale. Tecnicamente, HwndHost è una classe FrameworkElement derivata, non una classe Control derivata, ma può essere considerato un controllo ai fini dell'interoperatività. HwndHost estrae la natura Win32 sottostante del contenuto ospitato in modo tale che il resto di WPF consideri il contenuto ospitato come un altro oggetto simile a un controllo, che deve eseguire il rendering e l'elaborazione dell'input. HwndHost in genere ha un comportamento analogo a qualsiasi altro WPFFrameworkElement, anche se vi sono alcune differenze importanti relative all'output (disegno e grafica) e all'input (mouse e tastiera) in base alle limitazioni del supporto degli elementi HWND sottostanti.

Differenze rilevanti nel comportamento di output

  • FrameworkElement che costituisce la classe di base HwndHost ha alcune proprietà che implicano modifiche nell'interfaccia utente. Tra queste sono incluse proprietà quali FrameworkElement.FlowDirection che modifica il layout degli elementi all'interno dell'elemento come padre. Tuttavia, la maggior parte di queste proprietà non sono mappate a possibili equivalenti Win32, anche se tali equivalenti possono essere presenti. Poiché un numero troppo elevato di queste proprietà e dei relativi significati è troppo specifico della tecnologia di rendering, il mapping non è una soluzione pratica. Pertanto, l'impostazione di proprietà come FlowDirection su HwndHost non ha effetto.

  • Non è possibile ruotare, ridimensionare, inclinare, né applicare altri tipi di trasformazioni a HwndHost.

  • HwndHost non supporta la proprietà Opacity (alpha blending). Se il contenuto all'interno dell'oggetto HwndHost esegue operazioni System.Drawing che includono informazioni alpha, ciò non costituisce una violazione, ma l'oggetto HwndHost nel suo insieme supporta solo un valore di opacità pari a 1.0 (100%).

  • HwndHost verrà visualizzato sopra gli altri elementi WPF nella stessa finestra di livello principale. Tuttavia, un menu generato da ToolTip o ContextMenu è una finestra di primo livello separata e pertanto avrà un comportamento corretto con HwndHost.

  • HwndHost non rispetta l'area di visualizzazione dell'oggetto UIElement padre. Questa situazione costituisce un potenziale problema se si tenta di inserire una classe HwndHost all'interno di un'area di scorrimento o Canvas.

Differenze rilevanti nel comportamento di input

  • In genere, mentre l'ambito dei dispositivi di input è all'interno dell'area Win32 ospitata da HwndHost, gli eventi di input vanno direttamente in Win32.

  • Mentre il mouse si trova su HwndHost, l'applicazione non riceve gli eventi del mouse WPF e il valore della proprietà IsMouseOver WPF sarà false.

  • Mentre HwndHost ha lo stato attivo, l'applicazione non riceve gli eventi di tastiera WPF e il valore della proprietà IsKeyboardFocusWithin WPF sarà false.

  • Quando lo stato attivo è all'interno di HwndHost e passa a un altro controllo all'interno di HwndHost, l'applicazione non riceve gli eventi WPF GotFocus o LostFocus.

  • Proprietà ed eventi di stilo correlati sono analoghi e non comunicano informazioni mentre lo stilo si trova su HwndHost.

Tabulazioni, tasti di scelta e tasti di scelta rapida

Le interfacce IKeyboardInputSink e IKeyboardInputSite consentono l'utilizzo della tastiera senza problemi in applicazioni miste WPF e Win32:

  • Tabulazione tra componenti Win32 e WPF

  • I tasti di scelta e i tasti di scelta rapida funzionano sia quando lo stato attivo è all'interno di un componente Win32 sia quando è all'interno di un componente WPF.

Le classi HwndHost e HwndSource forniscono entrambe implementazioni di IKeyboardInputSink, ma potrebbero non essere in grado di gestire tutti i messaggi di input necessari in scenari più avanzati. Eseguire l'override dei metodi appropriati per ottenere il comportamento di tastiera desiderato.

Le interfacce offrono supporto solo per le operazioni eseguite nella transizione tra le aree WPF e Win32. All'interno dell'area Win32, il comportamento di tabulazione è completamente controllato dalla logica implementata da Win32 per la tabulazione, se presente.

Vedere anche

Riferimenti

HwndHost

HwndSource

System.Windows.Interop

Concetti

Procedura dettagliata: hosting di un controllo Win32 in WPF

Procedura dettagliata: hosting di contenuto WPF in Win32