Übersicht über die Interaktion zwischen WPF und Win32
Aktualisiert: November 2007
Dieses Thema enthält eine Übersicht über die Interaktion zwischen WPF-Code und Win32-Code. Windows Presentation Foundation (WPF) stellt eine umfangreiche Umgebung zum Erstellen von Anwendungen bereit. Wenn Sie allerdings bereits erheblichen Aufwand für Win32-Code betrieben haben, kann es effektiver sein, zumindest einen Teil dieses Codes wiederzuverwenden.
Dieses Thema enthält folgende Abschnitte.
- Grundlagen der Interaktion zwischen WPF und Win32
- WPF-Interaktionsprojekte
- Wie WPF HWNDs verwendet
- Hosten von WPF-Inhalt in einem Microsoft Win32-Fenster
- Hosten eines Microsoft Win32-Fensters in WPF
- TAB-TASTE und Zugriffstasten
- Verwandte Abschnitte
Grundlagen der Interaktion zwischen WPF und Win32
Es gibt zwei grundlegende Verfahren der Interaktion zwischen WPF-Code und Win32-Code.
Hosten von WPF-Inhalt in einem Win32-Fenster. Mit diesem Verfahren können Sie die erweiterten Grafikfunktionen von WPF im Rahmen standardmäßiger Win32-Fenster und -Anwendungen nutzen.
Hosten eines Win32-Fensters in WPF-Inhalt. Mit diesem Verfahren können Sie ein vorhandenes benutzerdefiniertes Win32-Steuerelement im Zusammenhang mit anderem WPF-Inhalt verwenden und Daten über die Grenzen hinweg übergeben.
In diesem Thema wird das Konzept beider Verfahren erläutert. Eine mehr am Code orientierte Erläuterung zum Hosten von WPF in Win32 finden Sie unter Exemplarische Vorgehensweise: Hosten eines einfachen Win32-Steuerelements in einer Windows Presentation Foundation-Anwendung. Eine mehr am Code orientierte Erläuterung zum Hosten von Win32 in WPF finden Sie unter Exemplarische Vorgehensweise: Hosten von Windows Presentation Foundation-Inhalt in einer Win32-Anwendung.
WPF-Interaktionsprojekte
Bei WPF APIs handelt es sich um verwalteten Code, die meisten vorhandenen Win32-Programme werden jedoch in nicht verwaltetem C++ geschrieben. Sie können WPF APIs nicht über ein echtes, nicht verwaltetes Programm aufrufen. Durch Verwendung der /clr-Option mit dem Microsoft Visual C++-Compiler können Sie jedoch eine Mischung aus verwaltetem und nicht verwaltetem Programm erstellen und dabei verwaltete und nicht verwaltete API-Aufrufe nahtlos mischen.
Eine Schwierigkeit auf Projektebene besteht darin, dass Sie Extensible Application Markup Language (XAML)-Dateien nicht in ein C++-Projekt kompilieren können. Dieses Problem lässt sich durch verschiedene Verfahren zur Projektaufteilung kompensieren.
Erstellen Sie eine C#-DLL, die alle XAML-Seiten als kompilierte Assembly enthält, und schließen Sie dann diese DLL als Verweis in die ausführbare C++-Datei ein.
Erstellen Sie eine ausführbare C#-Datei für den WPF-Inhalt, und lassen Sie sie auf eine C++DLL verweisen, die den Win32-Inhalt enthält.
Verwenden Sie Load, um XAML zur Laufzeit zu laden, anstatt XAML zu kompilieren.
Verwenden Sie überhaupt kein XAML, und schreiben Sie die gesamte WPF in Code, wobei die Elementstruktur über Application aufgebaut wird.
Verwenden Sie die für Sie am besten geeignete Vorgehensweise.
Tipp
Falls Sie C++/CLI zum ersten Mal verwenden, werden Ihnen möglicherweise einige "neue" Schlüsselwörter wie gcnew und nullptr in den Codebeispielen für die Interaktion auffallen. Diese Schlüsselwörter ersetzen die ältere Syntax mit doppeltem Unterstrich (__gc) und ermöglichen eine natürlichere Syntax für verwalteten Code in C++. Weitere Informationen zu den verwalteten C++/CLI-Features finden Sie unter Language Features for Targeting the CLR und Hello, C++/CLI.
Wie WPF HWNDs verwendet
Um WPF "HWND interop" optimal nutzen zu können, müssen Sie wissen, wie WPF HWNDs verwendet. Bei keinem HWND können Sie WPF-Rendering mit GDI+-Rendering oder DirectX/GDI-Rendering kombinieren. Dies hat verschiedene Auswirkungen. Um diese Renderingmodelle überhaupt kombinieren zu können, müssen Sie eine Interaktionslösung erstellen und festgelegte Interaktionssegmente für jedes gewählte Renderingmodell verwenden. Durch das Renderingverhalten wird außerdem eine "Airspace"-Beschränkung erzeugt, die die Funktionsfähigkeit der Interaktionslösung einschränkt. Das "Airspace"-Konzept wird im Thema WPF-Interoperation: "Airspace" und Übersicht über Fensterbereiche genauer erklärt.
Alle WPF-Elemente auf dem Bildschirm werden letztlich von einem HWND unterstützt. Wenn Sie ein WPF Window erstellen, erzeugt WPF ein HWND auf oberster Ebene und fügt das Window und dessen WPF-Inhalt mit HwndSource in das HWND ein. Dieses einzige HWND wird vom Rest des WPF-Inhalts in der Anwendung gemeinsam verwendet. Davon ausgenommen sind Menüs, Dropdown-Kombinationsfelder und andere Popupelemente. Diese Elemente erstellen ihr eigenes Fenster auf der obersten Ebene, weshalb ein WPF-Menü über das Fenster-HWND hinausgehen kann, in dem es enthalten ist. Wenn Sie ein HWND mit HwndHost in WPF einfügen, informiert WPFWin32, wie das neue untergeordnete HWND relativ zum WPF Window HWND positioniert werden soll.
Ein mit dem HWND verwandtes Konzept ist die Transparenz innerhalb und zwischen jedem HWND. Dies wird auch im Thema WPF-Interoperation: "Airspace" und Übersicht über Fensterbereiche erläutert.
Hosten von WPF-Inhalt in einem Microsoft Win32-Fenster
Entscheidend für das Hosten eines Win32 in einem WPF ist die HwndSource-Klasse. Diese Klasse schließt den WPF-Inhalt in ein Win32-Fenster ein, sodass der WPF-Inhalt als untergeordnetes Fenster in Ihre Benutzeroberfläche (user interface, UI) eingebunden werden kann. Bei folgender Vorgehensweise werden Win32 und WPF in einer einzigen Anwendung zusammengefasst.
Implementieren Sie den WPF-Inhalt (das Stammelement des Inhalts) als verwaltete Klasse. In der Regel erbt diese Klasse von einer der Klassen, die mehrere untergeordnete Elemente enthalten und/oder als Stammelement verwendet werden können, wie z. B. DockPanel oder Page. In nachfolgenden Schritten wird diese Klasse als WPF-Inhaltsklasse bezeichnet, und Instanzen dieser Klasse werden als WPF-Inhaltobjekte bezeichnet.
Implementieren Sie eine Win32-Anwendung mit C++/CLI. Wenn Sie eine vorhandene nicht verwaltete C++-Anwendung verwenden, können Sie diese in der Regel verwalteten Code aufrufen lassen, indem Sie das /clr-Compiler-Flag in die Projekteinstellungen aufnehmen (die Voraussetzungen zur Unterstützung der /clr-Kompilierung sind in diesem Thema nicht in vollem Umfang beschrieben).
Legen Sie das Threadingmodell auf Singlethread-Apartment (STA) fest. WPF verwendet dieses Threadingmodell.
Behandeln Sie die WM_CREATE-Benachrichtigung in der Fensterprozedur.
Gehen Sie im Handler (oder in einer Funktion, die vom Handler aufgerufen wird) wie folgt vor:
Erstellen Sie ein neues HwndSource-Objekt mit dem HWND des übergeordneten Fensters als dessen parent-Parameter.
Erstellen Sie eine Instanz der WPF-Inhaltsklasse.
Weisen Sie dem WPF-Inhaltsobjekt einen Verweis auf die RootVisual-Eigenschaft des HwndSource-Objekts zu.
Die Handle-Eigenschaft des HwndSource-Objekts enthält das Fensterhandle (HWND). Um ein HWND zu erhalten, das Sie im nicht verwalteten Teil der Anwendung verwenden können, wandeln Sie Handle.ToPointer() in ein HWND um.
Implementieren Sie eine verwaltete Klasse, die ein statisches Feld mit einem Verweis auf das WPF-Inhaltsobjekt enthält. Mithilfe dieser Klasse können Sie einen Verweis auf das WPF-Inhaltsobjekt aus dem Win32-Code erstellen, außerdem verhindert sie, dass das HwndSource versehentlich an die Garbage Collection übergeben wird.
Fügen Sie zum Empfangen von Benachrichtigungen vom WPF-Inhaltsobjekt an ein oder mehrere WPF-Inhaltsobjektereignisse einen Handler an.
Kommunizieren Sie mithilfe des im statischen Feld gespeicherten Verweises mit dem WPF-Inhaltsobjekt, um Eigenschaften festzulegen, Methoden aufzurufen usw.
Tipp
Sie können die Definition der WPF-Inhaltsklasse für Schritt 1 auch ganz oder teilweise mithilfe der standardmäßigen partiellen Klasse der Inhaltsklasse in XAML vornehmen, wenn Sie eine separate Assembly erzeugen und dann darauf verweisen. Obwohl Sie in der Regel ein Application-Objekt als Teil der Kompilierung von XAML in eine Assembly übernehmen, wird diese Application nicht als Teil der Interaktion verwendet. Sie verwenden nur eine oder mehrere der Stammklassen für XAML-Dateien, auf die von der Anwendung verwiesen wird, und verweisen auf ihre partiellen Klassen. Der Rest der Prozedur entspricht im Wesentlichen der oben beschriebenen.
Jeder dieser Schritte wird im Thema Exemplarische Vorgehensweise: Hosten von Windows Presentation Foundation-Inhalt in einer Win32-Anwendung anhand von Code veranschaulicht.
Hosten eines Microsoft Win32-Fensters in WPF
Entscheidend für das Hosten eines Win32-Fensters in anderem WPF-Inhalt ist die HwndHost-Klasse. Diese Klasse schließt das Fenster in ein WPF-Element ein, das einer WPF-Elementstruktur hinzugefügt werden kann. HwndHost unterstützt auch APIs zur Ausführung von Aufgaben wie der Verarbeitung von Meldungen für das gehostete Fenster. Die grundlegende Prozedur lautet wie folgt:
Erstellen Sie eine Elementstruktur für eine WPF-Anwendung (durch Code oder Markup möglich). Suchen Sie einen geeigneten und zulässigen Punkt in der Elementstruktur, an dem die HwndHost-Implementierung als untergeordnetes Element hinzugefügt werden kann. In den nachfolgenden Schritten wird dieses Element als reservierendes Element bezeichnet.
Erstellen Sie durch Ableiten von HwndHost ein Objekt mit dem Win32-Inhalt.
Überschreiben Sie in dieser Hostklasse die HwndHost-Methode BuildWindowCore. Geben Sie das HWND des gehosteten Fensters zurück. Möglicherweise ist es sinnvoll, die eigentlichen Steuerelemente als untergeordnetes Fenster des zurückgegebenen Fensters einzuschließen; durch Einschließen der Steuerelemente in ein Hostfenster kann der WPF-Inhalt Benachrichtigungen von den Steuerelementen erhalten. Diese Vorgehensweise hilft bei der Behebung einiger Win32-Probleme in Bezug auf die Meldungsbehandlung an der Begrenzung des gehosteten Steuerelements.
Überschreiben Sie die HwndHost-Methoden DestroyWindowCore und WndProc. Hiermit sollen eine Bereinigung durchgeführt und Verweise auf den gehosteten Inhalt entfernt werden, insbesondere dann, wenn Sie Verweise auf nicht verwaltete Objekte erstellt haben.
Erstellen Sie in der Code-Behind-Datei eine Instanz der Steuerelement-Hostingklasse, und machen Sie diese zu einem untergeordneten Element des reservierenden Elements. In der Regel wird ein Ereignishandler wie Loaded oder der Konstruktor der partiellen Klasse verwendet. Sie können den Interaktionsinhalt aber auch durch ein Laufzeitverhalten hinzufügen.
Verarbeiten Sie ausgewählte Fenstermeldungen wie Steuerelementbenachrichtigungen. Es gibt zwei Verfahrensweisen. Beide ermöglichen denselben Zugang zum Meldungsstream, Sie können also das Verfahren auswählen, das Ihnen einfacher erscheint.
Implementieren Sie die Meldungsverarbeitung für alle Meldungen (nicht nur Meldungen zum Herunterfahren) in der Überschreibung der HwndHost-Methode WndProc.
Lassen Sie das WPF-Hostingelement die Meldungen durch Behandeln des MessageHook-Ereignisses verarbeiten. Dieses Ereignis wird für jede Meldung ausgelöst, die an die Hauptfensterprozedur des gehosteten Fensters gesendet wird.
Sie können keine Meldungen von prozessexternen Fenstern mit WndProc verarbeiten.
Kommunizieren Sie mit dem gehosteten Fenster mithilfe eines Plattformaufrufs, um die nicht verwaltete SendMessage-Funktion aufzurufen.
Durch Befolgen dieser Schritte wird eine Anwendung erstellt, die per Mauseingabe funktioniert. Sie können das gehostete Fenster durch Implementieren der IKeyboardInputSink-Schnittstelle mit Unterstützung der TAB-Verwendung versehen.
Jeder dieser Schritte wird im Thema Exemplarische Vorgehensweise: Hosten eines einfachen Win32-Steuerelements in einer Windows Presentation Foundation-Anwendung anhand von Code veranschaulicht.
HWNDs in WPF
Sie können sich HwndHost als ein spezielles Steuerelement vorstellen. (Technisch gesehen ist HwndHost eine abgeleitete FrameworkElement-Klasse, keine abgeleitete Control-Klasse. Es kann jedoch als Steuerelement für Interaktionszwecke betrachtet werden.) HwndHost abstrahiert die zugrunde liegende Win32-Natur des gehosteten Inhalts, sodass der Rest von WPF den gehosteten Inhalt als ein anderes steuerelementartiges Objekt ansieht, das Eingaben rendern und verarbeiten soll. HwndHost verhält sich grundsätzlich wie jedes andere WPFFrameworkElement, in Bezug auf die Ausgabe (Zeichnung und Grafiken) und die Eingabe (Maus und Tastatur) basierend auf einer eingeschränkten Unterstützung durch HWNDs bestehen jedoch einige wichtige Unterschiede.
Wichtige Unterschiede im Ausgabeverhalten
FrameworkElement, die HwndHost-Basisklasse, verfügt über einige Eigenschaften, die Änderungen an der Benutzeroberfläche beinhalten. Hierzu gehören Eigenschaften wie FrameworkElement.FlowDirection, wodurch die Anordnung von Elementen innerhalb dieses Element als übergeordnetes Element geändert wird. Die meisten dieser Eigenschaften werden jedoch keinen möglichen Win32-Entsprechungen zugeordnet, selbst wenn solche Entsprechungen vorhanden sind. Zu viele dieser Eigenschaften und ihre Bedeutungen sind zu renderingspezifisch, als dass Zuordnungen praktisch wären. Deshalb hat das Festlegen von Eigenschaften wie FlowDirection auf HwndHost keine Auswirkungen.
HwndHost kann nicht gedreht, skaliert, verzerrt oder auf andere Weise durch eine Transformation geändert werden.
HwndHost bietet keine Unterstützung für die Opacity-Eigenschaft (Alphablending). Falls der Inhalt im HwndHostSystem.Drawing-Vorgänge ausführt, die Alphainformationen enthalten, stellt dies an sich keine Verletzung dar, von HwndHost als Ganzem wird jedoch nur eine Durchlässigkeit von 1,0 (100 %) unterstützt.
HwndHost wird über anderen WPF-Elementen in demselben Fenster auf oberster Ebene angezeigt. Ein mit ToolTip oder ContextMenu erstelltes Menü stellt jedoch ein separates Fenster auf oberster Ebene dar und verhält sich deshalb korrekt mit HwndHost.
HwndHost berücksichtigt den Auswahlbereich seines übergeordneten UIElement nicht. Dies stellt ein potenzielles Problem dar, wenn Sie versuchen, eine HwndHost-Klasse in einen Bildlaufbereich oder Canvas einzufügen.
Wichtige Unterschiede im Eingabeverhalten
Im Allgemeinen werden Eingabeereignisse bei Eingabegeräten innerhalb des von HwndHost gehosteten Win32-Bereichs direkt an Win32 weitergegeben.
Während sich die Maus über HwndHost befindet, empfängt die Anwendung keine WPF-Mausereignisse, und der Wert der WPF-Eigenschaft IsMouseOver ist false.
Während HwndHost Tastaturfokus hat, empfängt die Anwendung keine WPF-Tastaturereignisse, und der Wert der WPF-Eigenschaft IsKeyboardFocusWithin ist false.
Liegt der Fokus im HwndHost und wechselt zu einem anderen Steuerelement im HwndHost, werden die WPF-Ereignisse GotFocus oder LostFocus nicht von der Anwendung empfangen.
Entsprechende Tablettstifteigenschaften und -Ereignisse verhalten sich analog und geben keine Informationen aus, wenn sich der Tablettstift über HwndHost befindet.
TAB-TASTE und Zugriffstasten
Die IKeyboardInputSink-Schnittstelle und die IKeyboardInputSite-Schnittstelle ermöglichen eine nahtlose Tastatureinbindung bei gemischten WPF-Anwendungen und Win32-Anwendungen:
Wechseln zwischen Win32-Komponenten und WPF-Komponenten mithilfe der TAB-TASTE
Zugriffstasten funktionieren sowohl beim Fokus auf einer Win32-Komponente als auch auf einer WPF-Komponente.
Sowohl die HwndHost-Klasse als auch die HwndSource-Klasse stellen eine Implementierung von IKeyboardInputSink bereit, sie behandeln jedoch möglicherweise nicht alle Eingabemeldungen, die in komplexeren Szenarien erwünscht sind. Überschreiben Sie die entsprechenden Methoden, um das gewünschte Tastaturverhalten zu erhalten.
Die Schnittstellen unterstützen nur Vorgänge, die den Übergang zwischen dem WPF-Bereich und dem Win32-Bereich betreffen. Innerhalb des Win32-Bereichs wird das TAB-Verhalten ggf. vollständig durch die von Win32 implementierte Tabulatorreihenfolgenlogik gesteuert.