Interoperatività di WPF e Win32

Questo argomento offre una panoramica di come interagire con il codice Windows Presentation Foundation (WPF) e Win32. WPF offre un ambiente avanzato per la creazione di applicazioni. Tuttavia, quando si ha un notevole investimento nel codice Win32, potrebbe essere più efficace riutilizzare parte di tale codice.

Nozioni di base sull'interoperatività di WPF e Win32

Esistono due tecniche di base per l'interoperabilità tra codice WPF e Win32.

  • Ospitare contenuto WPF in una finestra Win32. Con questa tecnica, è possibile usare le funzionalità grafiche avanzate di WPF nel framework di una finestra e di un'applicazione Win32 standard.

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

Questo argomento illustra concettualmente queste due tecniche. Per un'illustrazione più orientata al codice dell'hosting di WPF in Win32, vedere Procedura dettagliata: Hosting di contenuto WPF in Win32. Per un'illustrazione più orientata al codice dell'hosting di Win32 in WPF, vedere Procedura dettagliata: Hosting di un controllo Win32 in WPF.

Progetti di interoperatività WPF

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

Una complicazione a livello di progetto è che non è possibile compilare file XAML (Extensible Application Markup Language) in un progetto C++. Per ovviare a tale problema, ci sono diverse tecniche di divisione dei progetti.

  • Creare una DLL C# contenente tutte le pagine XAML come assembly compilato e quindi includere la DLL come riferimento.

  • Creare un eseguibile C# per il contenuto WPF e fare riferimento a una DLL C++ che contiene il contenuto Win32.

  • Usa Load per caricare qualsiasi XAML in fase di esecuzione, invece di compilare il codice XAML.

  • Non usare XAML e scrivere tutto il codice WPF nel codice, creando l'albero degli elementi da Application.

Usare l'approccio che meglio soddisfa le esigenze.

Nota

Se in precedenza non è stato usato C++/CLI, è possibile notare alcune parole chiave "new", ad gcnew esempio e nullptr negli esempi di codice di interoperabilità. Queste parole chiave sostituisce la sintassi di sottolineatura doppia precedente (__gc) e forniscono una sintassi più naturale per il codice gestito in C++. Per altre informazioni sulle funzionalità gestite da C++/CLI, vedere Estensioni dei componenti per piattaforme di runtime.

Modo d'uso degli handle di finestra (HWND) in WPF

Per sfruttare al meglio l'interoperabilità "HWND" di WPF, è necessario comprendere in che modo WPF usa HWND. Per qualsiasi HWND, non è possibile combinare il rendering WPF con il rendering DirectX o il rendering GDI/GDI+. Questo comporta diverse implicazioni. In primo luogo, per combinare questi modelli di rendering, è necessario creare una soluzione di interoperatività e usare segmenti designati di interoperatività per ogni modello di rendering che si sceglie di usare. Il comportamento di rendering crea inoltre una restrizione di "spazio aereo" per le operazioni che la soluzione di interoperatività può completare. Il concetto di "spazio aereo" è illustrato più dettagliatamente nell'argomento Cenni preliminari sulle aree di tecnologia.

Tutti gli elementi WPF sullo schermo sono supportati da un HWND. Quando si crea un wpf Window, WPF crea un HWND di primo livello e usa un HwndSource oggetto per inserire e Window il relativo contenuto WPF all'interno di HWND. Il resto del contenuto WPF nell'applicazione condivide tale singolo HWND. Un'eccezione è costituita da menu, caselle combinate a discesa e altri popup. Questi elementi creano la propria finestra di primo livello, motivo per cui un menu WPF può potenzialmente superare il bordo della finestra HWND che lo contiene. Quando si usa HwndHost per inserire un HWND in WPF, WPF informa Win32 come posizionare il nuovo HWND figlio rispetto a WPF Window HWND.

Un concetto correlato a HWND è la trasparenza all'interno di ogni oggetto HWND e tra tali oggetti. Questo concetto è illustrato anche nell'argomento Cenni preliminari sulle aree di tecnologia.

Hosting di contenuto WPF in una finestra Microsoft Win32

La chiave per ospitare un wpf in una finestra Win32 è la HwndSource classe . Questa classe esegue il wrapping del contenuto WPF in una finestra Win32, in modo che il contenuto WPF possa essere incorporato nell'interfaccia utente come finestra figlio. L'approccio seguente combina Win32 e WPF in una singola applicazione.

  1. Implementare il contenuto WPF (l'elemento radice del contenuto) come classe gestita. In genere, la classe eredita da una delle classi che possono contenere più elementi figlio e/o usati come elemento radice, ad esempio DockPanel o Page. Nei passaggi successivi questa classe viene definita classe di contenuto WPF e le istanze della classe vengono definite oggetti contenuto WPF.

  2. Implementare un'applicazione Windows con C++/CLI. Se si inizia con un'applicazione C++ non gestita esistente, in genere è possibile abilitarla per chiamare il codice gestito modificando le impostazioni del progetto in modo da includere il /clr flag del compilatore (l'ambito completo di ciò che potrebbe essere necessario per supportare /clr la compilazione non è descritto in questo argomento).

  3. Impostare il modello di threading su apartment a thread singolo (STA, Single Threaded Apartment). WPF usa questo modello di threading.

  4. Gestire la notifica WM_CREATE nella procedura di finestra.

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

    1. Creare un nuovo HwndSource oggetto con la finestra padre HWND come parent parametro.

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

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

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

  6. Implementare una classe gestita che contiene un campo statico che contiene un riferimento all'oggetto contenuto WPF. Questa classe consente di ottenere un riferimento all'oggetto contenuto WPF dal codice Win32, ma soprattutto impedisce che il HwndSource garbage collection venga accidentalmente sottoposto a Garbage Collection.

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

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

Nota

È possibile eseguire alcune o tutte le definizioni della classe di contenuto WPF per Step One in XAML usando la classe parziale predefinita della classe di contenuto, se si produce un assembly separato e quindi vi si fa riferimento. Anche se in genere includi un Application oggetto come parte della compilazione del codice XAML in un assembly, non finirai di usarlo Application come parte dell'interoperabilità, usa solo una o più classi radice per i file XAML a cui fa riferimento l'applicazione e fai riferimento alle relative classi parziali. La parte restante della procedura è essenzialmente simile a quella appena descritta.

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

Hosting di una finestra Microsoft Win32 in WPF

La chiave per ospitare una finestra Win32 all'interno di altri contenuti WPF è la HwndHost classe . Questa classe esegue il wrapping della finestra in un elemento WPF che può essere aggiunto a un albero degli elementi WPF. HwndHost supporta anche le API che consentono di eseguire attività quali elaborare i messaggi per la finestra ospitata. La procedura di base è la seguente:

  1. Creare un albero degli elementi per un'applicazione WPF (può essere tramite codice o markup). Trovare un punto appropriato e consentito nell'albero degli elementi in cui è possibile aggiungere l'implementazione HwndHost come elemento figlio. Nei restanti passaggi questo elemento viene chiamato elemento di riserva.

  2. Derivare da HwndHost per creare un oggetto che contiene il contenuto Win32.

  3. In tale classe host eseguire l'override del HwndHost metodo BuildWindowCore. Restituire l'oggetto HWND della finestra ospitata. È possibile eseguire il wrapping dei controlli effettivi come finestra figlio della finestra restituita; il wrapping dei controlli in una finestra host consente al contenuto WPF di ricevere notifiche dai controlli. Questa tecnica consente di correggere alcuni problemi win32 relativi alla gestione dei messaggi al limite del controllo ospitato.

  4. Eseguire l'override dei HwndHost metodi DestroyWindowCore e WndProcdi . Lo scopo è quello di eseguire la pulizia e rimuovere i 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 impostarla come figlio dell'elemento di riserva. In genere si usa un gestore eventi, ad Loadedesempio , o si usa il costruttore parziale della classe. È però anche possibile aggiungere il contenuto di interoperatività tramite un comportamento di runtime.

  6. Elaborare i messaggi della finestra selezionati, ad esempio le notifiche dei controlli. Ci sono due approcci. Entrambi offrono un accesso identico al flusso di messaggi, quindi la scelta dipende per lo più dalle esigenze di programmazione.

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

    • Chiedere all'elemento WPF di hosting di elaborare i messaggi gestendo l'evento MessageHook . Questo evento viene generato per ogni messaggio inviato alla procedura di finestra principale della finestra ospitata.

    • Non è possibile elaborare messaggi da finestre non elaborate usando WndProc.

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

Questi passaggi consentono di creare un'applicazione che funziona con l'input del mouse. È possibile aggiungere il supporto della tabulazione per la finestra ospitata implementando l'interfaccia IKeyboardInputSink .

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

Handle di finestra (HWND) in WPF

Si può pensare a HwndHost come un controllo speciale. Tecnicamente, HwndHost è una FrameworkElement classe derivata, non una Control classe derivata, ma può essere considerata un controllo a scopo di interoperabilità. HwndHost Astrae la natura Win32 sottostante del contenuto ospitato in modo che il resto di WPF consideri il contenuto ospitato come un altro oggetto simile a un controllo, che deve eseguire il rendering ed elaborare l'input. HwndHost in genere si comporta come qualsiasi altro WPF FrameworkElement, anche se esistono alcune differenze importanti per l'output (disegno e grafica) e l'input (mouse e tastiera) in base alle limitazioni di ciò che i HWND sottostanti possono supportare.

Differenze rilevanti nel comportamento di output

  • FrameworkElement, che è la HwndHost classe di base, ha alcune proprietà che implicano modifiche all'interfaccia utente. Sono incluse proprietà come FrameworkElement.FlowDirection, che modificano il layout degli elementi all'interno di tale elemento come elemento padre. Tuttavia, la maggior parte di queste proprietà non è mappata a possibili equivalenti Win32, anche se tali equivalenti potrebbero esistere. Poiché un numero 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 alcun effetto.

  • HwndHost non può essere ruotato, ridimensionato, asimmetrico o interessato da una trasformazione.

  • HwndHost non supporta la Opacity proprietà (fusione alfa). Se il contenuto all'interno di HwndHost esegue System.Drawing operazioni che includono informazioni alfa, non si tratta di una violazione, ma nel HwndHost suo complesso supporta solo Opacity = 1,0 (100%).

  • HwndHost verrà visualizzato sopra altri elementi WPF nella stessa finestra di primo livello. Tuttavia, un ToolTip menu o ContextMenu generato è una finestra di primo livello separata e quindi si comporta correttamente con HwndHost.

  • HwndHost non rispetta l'area di ritaglio del relativo elemento padre UIElement. Questo è potenzialmente un problema se si tenta di inserire una HwndHost classe all'interno di un'area di scorrimento o Canvas.

Differenze rilevanti nel comportamento di input

  • In generale, mentre i dispositivi di input sono inclusi nell'ambito dell'area HwndHost Win32 ospitata, gli eventi di input passano direttamente a Win32.

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

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

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

  • Le proprietà e gli eventi dello stilo correlati sono analoghi e non segnalano informazioni mentre lo stilo è su HwndHost.

Tabulazioni, tasti di scelta e tasti di scelta rapida

Le IKeyboardInputSink interfacce e IKeyboardInputSite consentono di creare un'esperienza facile da tastiera per applicazioni WPF e Win32 miste:

  • Tabulazione tra componenti Win32 e WPF

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

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

Le interfacce forniscono supporto solo per ciò che accade durante la transizione tra le aree WPF e Win32. All'interno dell'area Win32, il comportamento di tabulazione è interamente controllato dalla logica implementata da Win32 per la tabulazione, se presente.

Vedi anche