Condividi tramite


Interoperabilità 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'interoperabilità 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.

Ognuna di queste tecniche è concettualmente introdotta in questo argomento. Per un esempio più incentrato sul codice dell'hosting di WPF in Win32, vedere Procedura dettagliata: Hosting di contenuto WPF in Win32. Per un esempio più orientato al codice dell'hosting di un controllo Win32 in WPF, vedere Procedura dettagliata: Hosting di un controllo Win32 in WPF.

Progetti di interoperabilità 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 misto non gestito 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++. Esistono diverse tecniche di divisione del progetto per compensare questo problema.

  • Creare una DLL C# contenente tutte le pagine XAML come assembly compilato, e quindi fare in modo che il tuo eseguibile C++ includa quella 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 utilizzare affatto XAML e scrivere tutto il tuo WPF nel codice, costruendo l'albero degli elementi a partire da Application.

Usare qualsiasi approccio funzioni al meglio per te.

Nota

Se in precedenza non è stato usato C++/CLI, è possibile notare alcune parole chiave "nuove", ad esempio gcnew 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 Component Extensions for Runtime Platforms.

Modalità di utilizzo di Hwnds 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 ha diverse implicazioni. Principalmente, per combinare questi modelli di rendering, è necessario creare una soluzione di interoperabilità e usare segmenti designati di interoperabilità per ogni modello di rendering che si sceglie di usare. Inoltre, il comportamento di rendering crea una restrizione di "spazio operativo" per quello che la vostra soluzione di interoperabilità può ottenere. Il concetto di "spazio aereo" è illustrato in modo più dettagliato nell'argomento panoramica delle aree tecnologiche .

Tutti gli elementi WPF sullo schermo sono supportati da un HWND. Quando si crea un WindowWPF, WPF crea un HWND di primo livello e usa un HwndSource per inserire il Window e il relativo contenuto WPF all'interno di HWND. Il resto del contenuto WPF nell'applicazione condivide tale singolo HWND. Le eccezioni sono i menu, i menu a discesa delle caselle combinate 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 comunica a Win32 come posizionare il nuovo HWND figlio rispetto a WPF Window HWND.

Un concetto correlato a HWND è la trasparenza all'interno e tra ogni HWND. Questo argomento è illustrato anche nell'argomento panoramica delle aree tecnologiche .

Hosting di contenuto WPF in una finestra di Microsoft Win32

La chiave per ospitare un wpf in una finestra Win32 è la classe HwndSource. Questa classe racchiude il contenuto WPF in una finestra Win32, in modo che il contenuto WPF possa essere incorporato nell'interfaccia utente come finestra figlia. 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 flag del compilatore /clr (l'ambito completo di ciò che potrebbe essere necessario per supportare la compilazione /clr non è descritto in questo argomento).

  3. Impostare il modello di threading su "Single Threaded Apartment" (STA). WPF usa 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 la finestra padre HWND come parametro parent.

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

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

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

  6. Implementare una classe gestita che contiene un campo statico che contiene un riferimento all'oggetto di contenuto WPF. Questa classe consente di ottenere un riferimento all'oggetto di contenuto WPF dal codice Win32, ma, soprattutto, impedisce che il HwndSource venga inavvertitamente rimosso dal processo di 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 di contenuto WPF utilizzando il riferimento che hai 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 oggetto Application come parte della compilazione del codice XAML in un assembly, non utilizzi quel Application nell'interoperabilità. Usi solo una o più classi radice per i file XAML a cui fa riferimento l'applicazione e ne riferisci le classi parziali. Il resto della procedura è essenzialmente simile a quello descritto in precedenza.

Ognuno di questi passaggi è illustrato tramite codice nel topic Procedura dettagliata: Hosting di contenuto WPF in Win32.

Ospitare una finestra Microsoft Win32 in WPF

La chiave per ospitare una finestra Win32 all'interno di altri contenuti WPF è la classe HwndHost. 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à come elaborare i messaggi per la finestra ospitata. La procedura di base è:

  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. Nella parte restante di questi passaggi, questo elemento viene definito elemento di prenotazione.

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

  3. Nella classe host in questione, eseguire l'override del metodo HwndHostBuildWindowCore. Restituisce il valore HWND della finestra ospitata. Potresti voler eseguire il wrapping dei controlli effettivi come finestre figlie della finestra restituente; avvolgere i controlli in una finestra host fornisce un modo semplice per consentire 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. Effettuare l'override dei metodi HwndHost, DestroyWindowCore e WndProc. L'intenzione è quella di elaborare la pulizia 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 host del controllo e impostarla come figlio dell'elemento di riserva. In genere si utilizza un gestore eventi, come ad esempio Loaded, oppure si ricorre al costruttore parziale della classe. Ma è anche possibile aggiungere il contenuto di interoperabilità tramite un comportamento di runtime.

  6. Elaborare i messaggi della finestra selezionati, ad esempio le notifiche di controllo. Esistono due approcci. Entrambi forniscono accesso identico al flusso di messaggi, quindi la scelta è in gran parte una questione di praticità di programmazione.

    • Implementare l'elaborazione dei messaggi per tutti i messaggi (non solo per i messaggi di arresto) nella ridefinizione del metodo HwndHostWndProc.

    • Chiedere all'elemento WPF di hosting di elaborare 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 che sono fuori dal processo usando WndProc.

  7. Comunicare con la finestra ospitata utilizzando Platform Invoke per chiamare la funzione SendMessage non gestita.

Seguendo questa procedura viene creata 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.

Hwnds all'interno di WPF

HwndHost può essere considerato un controllo speciale. Tecnicamente, HwndHost è una classe derivata FrameworkElement, non una classe derivata Control, ma può essere considerata un controllo ai fini dell'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 e l'elaborazione dell'input. HwndHost in genere si comporta come qualsiasi altro FrameworkElementWPF, anche se esistono alcune importanti differenze rispetto all'output (disegno e grafica) e all'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 classe base HwndHost, presenta alcune proprietà che implicano modifiche all'interfaccia utente. Queste includono proprietà come FrameworkElement.FlowDirection, che modifica 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. Troppe di queste proprietà e i loro significati sono troppo specifici della tecnologia di rendering perché le mappature siano pratiche. Pertanto, l'impostazione di proprietà come FlowDirection su HwndHost non ha alcun effetto.

  • HwndHost non può essere ruotato, ridimensionato, inclinato o altrimenti influenzato da una Trasformazione.

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

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

  • HwndHost non rispetta l'area di ritaglio del suo elemento padre UIElement. Questo è potenzialmente un 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 generale, mentre i dispositivi di input sono limitati alla regione Win32 ospitata da HwndHost, gli eventi di input passano direttamente a Win32.

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

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

  • Quando il focus si trova all'interno del HwndHost e cambia su un altro controllo all'interno del HwndHost, l'applicazione non riceverà gli eventi WPF GotFocus o LostFocus.

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

Tabulazione, Mnemoniche e Acceleratori

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

  • Tabulazione tra componenti Win32 e WPF

  • Tasti mnemonici e acceleratori che funzionano sia quando lo stato attivo si trova all'interno di un componente Win32 sia quando si trova all'interno di un componente WPF.

Le classi HwndHost 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 della 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.

Vedere anche