WPF- und Win32-Interoperabilität
Dieses Thema enthält eine Übersicht über die Interoperabilität von Windows Presentation Foundation (WPF) und Win32-Code. WPF bietet eine umfangreiche Umgebung zum Erstellen von Anwendungen. Wenn Sie jedoch eine erhebliche Investition in Win32-Code haben, ist es möglicherweise effektiver, einige dieser Code wiederzuverwenden.
Grundlagen der WPF- und Win32-Interoperabilität
Es gibt zwei grundlegende Techniken für die Interoperabilität zwischen WPF- und Win32-Code.
Einbetten von WPF-Inhalt in einem Win32-Fenster. Mit dieser Technik können Sie die erweiterten Grafikfunktionen von WPF im Rahmen eines Win32-Standardfensters und einer Standardanwendung verwenden.
Ein Win32-Fenster in WPF-Inhalt einbinden. Mit dieser Technik können Sie ein vorhandenes benutzerdefiniertes Win32-Steuerelement im Kontext anderer WPF-Inhalte verwenden und Daten über die Grenzen hinweg übergeben.
Jede dieser Techniken wird konzeptionell in diesem Thema eingeführt. Für eine codeorientiertere Darstellung des Hostings von WPF in Win32, siehe Walkthrough: Hosting WPF Content in Win32. Eine mehr auf Code ausgerichtete Darstellung des Hostings von Win32 in WPF finden Sie unter Walkthrough: Hosting a Win32 Control in WPF.
WPF-Interoperabilitätsprojekte
WPF-APIs sind verwalteter Code, aber die meisten vorhandenen Win32-Programme werden in nicht verwaltetem C++ geschrieben. Sie können WPF-APIs nicht aus einem echten nicht verwalteten Programm aufrufen. Mithilfe der Option /clr
mit dem Microsoft Visual C++-Compiler können Sie jedoch ein gemischtes, nicht verwaltetes Programm erstellen, in dem Sie verwaltete und nicht verwaltete API-Aufrufe nahtlos kombinieren können.
Eine Komplikation auf Projektebene besteht darin, dass Sie extensible Application Markup Language (XAML)-Dateien nicht in ein C++-Projekt kompilieren können. Es gibt mehrere Projektteilungstechniken, um dies zu kompensieren.
Erstellen Sie eine C#-DLL, die alle XAML-Seiten als kompilierte Assembly enthält, und lassen Sie dann die ausführbare Datei C++ diese DLL als Referenz enthalten.
Erstellen Sie eine ausführbare C#-Datei für den WPF-Inhalt, und verweisen Sie auf eine C++-DLL, 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 das gesamte WPF im Code, indem Sie die Elementstruktur aus Applicationaufbauen.
Verwenden Sie den für Sie am besten geeigneten Ansatz.
Anmerkung
Wenn Sie C++/CLI noch nicht verwendet haben, stellen Sie möglicherweise einige "neue" Schlüsselwörter wie gcnew
und nullptr
in den Codebeispielen für die Interoperabilität fest. Diese Schlüsselwörter ersetzen die ältere Syntax mit doppeltem Unterstrich (__gc
) und stellen eine natürlichere Syntax für verwalteten Code in C++ bereit. Weitere Informationen zu den verwalteten C++/CLI-Features finden Sie unter Komponentenerweiterungen für Runtime-Plattformen.
So verwendet WPF Hwnds
Um WPF "HWND interop" optimal zu nutzen, müssen Sie verstehen, wie WPF HWNDs verwendet. Für jedes HWND können Sie das WPF-Rendering nicht mit DirectX-Rendering oder GDI/GDI+-Rendering kombinieren. Dies hat eine Reihe von Auswirkungen. Um diese Renderingmodelle überhaupt kombinieren zu können, müssen Sie eine Interoperabilitätslösung erstellen und für jedes Renderingmodell, das Sie einsetzen, bestimmte Segmente der Interoperabilität nutzen. Außerdem führt das Renderingverhalten zu einer "Luftraumeinschränkung" für das, was Ihre Interoperabilitätslösung erreichen kann. Das Konzept "Luftraum" wird im Thema Technology Regions Overviewausführlicher erläutert.
Alle WPF-Elemente auf dem Bildschirm werden letztendlich von einem HWND unterstützt. Wenn Sie ein WPF-Windowerstellen, erstellt WPF ein HWND auf höchster Ebene und verwendet eine HwndSource, um den Window mit seinem WPF-Inhalt im HWND zu platzieren. Der Rest des WPF-Inhalts in der Anwendung teilt dieses einzige HWND. Eine Ausnahme sind Menüs, Kombinationsfeld-Dropdowns und andere Popups. Diese Elemente erstellen ein eigenes Fenster auf oberster Ebene. Deshalb kann ein WPF-Menü möglicherweise über den Rand des Fensters HWND hinausgehen, das es enthält. Wenn Sie HwndHost verwenden, um einen HWND in WPF einzufügen, informiert WPF Win32, wie der neue untergeordnete HWND relativ zum WPF-Window HWND positioniert wird.
Ein verwandtes Konzept zu HWND ist die Transparenz innerhalb eines jeden HWND sowie zwischen ihnen. Dies wird auch im Thema Überblick über Technologie-Regionenerläutert.
Hosten von WPF-Inhalten in einem Microsoft Win32-Fenster
Der Schlüssel zum Hosten eines WPF in einem Win32-Fenster ist die HwndSource Klasse. Diese Klasse umschließt den WPF-Inhalt in einem Win32-Fenster, sodass der WPF-Inhalt als untergeordnetes Fenster in die Benutzeroberfläche integriert werden kann. Der folgende Ansatz kombiniert win32 und WPF in einer einzigen Anwendung.
Implementieren Sie Ihren WPF-Inhalt (das Inhaltsstammelement) als verwaltete Klasse. In der Regel erbt die Klasse von einer der Klassen, die mehrere untergeordnete Elemente enthalten können und/oder als Wurzelelement verwendet werden, z. B. DockPanel oder Page. In nachfolgenden Schritten wird diese Klasse als WPF-Inhaltsklasse bezeichnet, und Instanzen der Klasse werden als WPF-Inhaltsobjekte bezeichnet.
Implementieren Sie eine Windows-Anwendung mit C++/CLI. Wenn Sie mit einer vorhandenen nicht verwalteten C++-Anwendung beginnen, können Sie diese normalerweise so einstellen, dass sie verwalteten Code aufruft, indem Sie die Projekteinstellungen ändern, um das Compiler-Flag
/clr
einzuschließen (der vollständige Umfang der Maßnahmen, die möglicherweise erforderlich sind, um die/clr
-Kompilierung zu unterstützen, wird in diesem Thema nicht beschrieben).Legen Sie das Threading-Modell auf Single Threaded Apartment (STA) fest. WPF verwendet dieses Threadingmodell.
Behandeln Sie die WM_CREATE-Nachricht in Ihrer Fensterprozedur.
Führen Sie innerhalb des Handlers (oder einer Funktion, die der Handler aufruft) die folgenden Schritte aus:
Erstellen Sie ein neues HwndSource-Objekt mit dem übergeordneten Fenster HWND als
parent
-Parameter.Erstellen Sie eine Instanz Ihrer WPF-Inhaltsklasse.
Weisen Sie dem HwndSource-Objekt RootVisual-Eigenschaft einen Verweis auf das WPF-Inhaltsobjekt zu.
Das HwndSource Objekt Handle Eigenschaft enthält das Fensterhandle (HWND). Um einen HWND zu erhalten, den Sie im nicht verwalteten Teil Ihrer Anwendung verwenden können, wandeln Sie
Handle.ToPointer()
in einen HWND um.
Implementieren Sie eine verwaltete Klasse, die ein statisches Feld enthält, das einen Verweis auf Ihr WPF-Inhaltsobjekt enthält. Mit dieser Klasse können Sie einen Verweis auf das WPF-Inhaltsobjekt aus dem Win32-Code abrufen, aber noch wichtiger ist, dass Ihr HwndSource nicht versehentlich von der Garbage Collection erfasst wird.
Empfangen von Benachrichtigungen vom WPF-Inhaltsobjekt durch Anfügen eines Handlers an eines oder mehrere der WPF-Inhaltsobjektereignisse.
Kommunizieren Sie mit dem WPF-Inhaltsobjekt mithilfe des Verweises, den Sie im statischen Feld gespeichert haben, um Eigenschaften, Aufrufmethoden usw. festzulegen.
Anmerkung
Sie können einige oder alle WPF-Inhaltsklassendefinitionen für Schritt 1 in XAML mithilfe der Standardteilklasse der Inhaltsklasse ausführen, wenn Sie eine separate Assembly erstellen und dann darauf verweisen. Obwohl Sie in der Regel ein Application-Objekt als Teil der Kompilierung des XAML-Codes in eine Assembly einschließen, nutzen Sie dieses Application nicht als Teil der Interoperabilität. Stattdessen verwenden Sie lediglich eine oder mehrere der Stammklassen für XAML-Dateien, auf die von der Anwendung verwiesen wird, und greifen auf ihre Teilklassen zu. Der Rest des Verfahrens ähnelt im Wesentlichen dem oben beschriebenen Verfahren.
Jede dieser Schritte wird durch Code im Thema Walkthrough: Hosting WPF Content in Win32veranschaulicht.
Hosten eines Microsoft Win32-Fensters in WPF
Der Schlüssel zum Hosten eines Win32-Fensters in anderen WPF-Inhalten ist die HwndHost Klasse. Diese Klasse umschließt das Fenster in einem WPF-Element, das einer WPF-Elementstruktur hinzugefügt werden kann. HwndHost unterstützt auch APIs, mit denen Sie aufgaben wie z. B. Verarbeiten von Nachrichten für das gehostete Fenster ausführen können. Das grundlegende Verfahren lautet:
Erstellen Sie einen Elementbaum für eine WPF-Anwendung (dies kann über Code oder Markup erfolgen). Suchen Sie einen geeigneten und zulässigen Punkt im Elementbaum, an dem die HwndHost Implementation als untergeordnetes Element hinzugefügt werden kann. In den verbleibenden Schritten wird dieses Element als das Reservierungselement bezeichnet.
Leiten Sie von HwndHost ab, um ein Objekt zu erstellen, das Ihren Win32-Inhalt enthält.
Überschreiben Sie in dieser Hostklasse die HwndHost-Methode BuildWindowCore. Gibt den HWND des gehosteten Fensters zurück. Möglicherweise möchten Sie das tatsächliche Steuerelement(n) als untergeordnetes Fenster des zurückgegebenen Fensters umschließen. Das Umschließen der Steuerelemente in einem Hostfenster bietet eine einfache Möglichkeit, wie WPF-Inhalte Benachrichtigungen von den Steuerelementen empfangen können. Diese Technik hilft, einige Win32-Probleme bei der Nachrichtenbehandlung an der Grenze des gehosteten Steuerelements zu korrigieren.
Überschreiben Sie die HwndHost Methoden DestroyWindowCore und WndProc. Dies ist die Absicht, Verweise auf den gehosteten Inhalt zu bereinigen und zu entfernen, insbesondere wenn Sie Verweise auf nicht verwaltete Objekte erstellt haben.
Erstellen Sie in Ihrer Code-behind-Datei eine Instanz der Steuerelement-Hostingklasse und machen Sie sie zu einem untergeordneten Element des reservierenden Elements. In der Regel würden Sie einen Ereignishandler wie Loadedverwenden oder den partiellen Klassenkonstruktor verwenden. Sie können den Interoperabilitätsinhalt aber auch über ein Laufzeitverhalten hinzufügen.
Verarbeiten von ausgewählten Fenstermeldungen, z. B. Steuerelementbenachrichtigungen. Es gibt zwei Ansätze. Beide bieten identischen Zugriff auf den Nachrichtendatenstrom, daher ist Ihre Wahl weitgehend eine Frage der Programmierfreundlichkeit.
Implementieren Sie die Nachrichtenverarbeitung für alle Nachrichten (nicht nur Herunterfahren von Nachrichten) in Ihrer Außerkraftsetzung der HwndHost-Methode WndProc.
Lassen Sie das WPF-Hostelement die Nachrichten verarbeiten, indem Sie das Ereignis MessageHook behandeln. Dieses Ereignis wird für jede Nachricht ausgelöst, die an die Hauptfensterprozedur des gehosteten Fensters gesendet wird.
Sie können Nachrichten von Fenstern, die sich außerhalb des Prozesses befinden, nicht mit WndProcverarbeiten.
Kommunizieren Sie mit dem gehosteten Fenster mithilfe des Plattformaufrufs, um die nicht verwaltete
SendMessage
-Funktion aufzurufen.
Wenn Sie diese Schritte ausführen, wird eine Anwendung erstellt, die mit Mauseingaben funktioniert. Sie können die Registerkartenunterstützung für Ihr gehostetes Fenster hinzufügen, indem Sie die IKeyboardInputSink-Schnittstelle implementieren.
Jede dieser Schritte wird durch Code im Thema Walkthrough: Hosting a Win32 Control in WPFveranschaulicht.
Hwnds Inside WPF
Sie können sich HwndHost als spezielles Steuerelement vorstellen. (Technisch gesehen ist HwndHost eine FrameworkElement abgeleitete Klasse, keine Control abgeleitete Klasse, kann aber als Steuerelement zu Zwecken der Interoperabilität betrachtet werden.) HwndHost abstrahiert die zugrunde liegende Win32-Natur des gehosteten Inhalts so, dass der Rest von WPF den gehosteten Inhalt als ein anderes Steuerelementähnliches Objekt betrachtet, das rendern und Eingaben verarbeiten sollte. HwndHost verhält sich im Allgemeinen wie jede andere WPF-FrameworkElement, obwohl es einige wichtige Unterschiede bei der Ausgabe (Zeichnen und Grafik) und Eingabe (Maus und Tastatur) gibt, basierend auf den Einschränkungen der zugrunde liegenden HWNDs.
Wichtige Unterschiede beim Ausgabeverhalten
FrameworkElement, bei dem es sich um die HwndHost Basisklasse handelt, verfügt über einige Eigenschaften, die Änderungen an der Benutzeroberfläche bedeuten. Dazu gehören Eigenschaften wie FrameworkElement.FlowDirection, die das Layout von Elementen innerhalb dieses übergeordneten Elements ändern. Die meisten dieser Eigenschaften sind jedoch nicht möglichen Win32-Entsprechungen zugeordnet, auch wenn solche Entsprechungen vorhanden sein könnten. Zu viele dieser Eigenschaften und ihre Bedeutungen sind zu spezifisch für Rendering-Technologien, um Zuordnungen sinnvoll zu machen. Das Festlegen von Eigenschaften wie FlowDirection für HwndHost hat daher keine Auswirkung.
HwndHost kann nicht gedreht, skaliert, verzerrt oder anderweitig durch eine Transformation beeinflusst werden.
HwndHost unterstützt die Opacity-Eigenschaft (Alphamischung) nicht. Wenn Inhalte innerhalb der HwndHostSystem.Drawing Vorgänge ausführen, die Alphainformationen enthalten, stellt das an sich keine Verletzung dar, aber die HwndHost als Ganzes unterstützt nur Opazität = 1.0 (100%).
HwndHost wird über anderen WPF-Elementen im gleichen obersten Fenster angezeigt. Ein durch ToolTip oder ContextMenu generiertes Menü ist jedoch ein separates Fenster auf oberster Hierarchieebene und verhält sich daher korrekt mit HwndHost.
HwndHost respektiert den Schnittbereich des übergeordneten UIElementnicht. Dies könnte ein Problem darstellen, wenn Sie versuchen, eine HwndHost-Klasse innerhalb eines Bildlaufbereichs oder in Canvaszu platzieren.
Wichtige Unterschiede beim Eingabeverhalten
Im Allgemeinen sind Eingabegeräte innerhalb der von HwndHost gehosteten Win32-Region eingegrenzt, und Eingabeereignisse gehen direkt zu Win32.
Während sich die Maus über dem HwndHostbefindet, empfängt Ihre Anwendung keine WPF-Mausereignisse, und der Wert der WPF-Eigenschaft IsMouseOver wird
false
.Während HwndHost den Tastaturfokus hat, erhält Ihre Anwendung keine WPF-Tastaturereignisse, und der Wert der WPF-Eigenschaft IsKeyboardFocusWithin ist
false
.Wenn sich der Fokus innerhalb des HwndHost befindet und auf ein anderes Steuerelement innerhalb der HwndHostverschiebt, empfängt Ihre Anwendung weder die WPF-Ereignisse GotFocus noch LostFocus.
Verwandte Eingabestifteigenschaften und Ereignisse sind analog und melden keine Informationen, während der Eingabestift über HwndHostliegt.
Tabbing, Mnemonics und Beschleuniger
Mit den schnittstellen IKeyboardInputSink und IKeyboardInputSite können Sie eine nahtlose Tastaturumgebung für gemischte WPF- und Win32-Anwendungen erstellen:
Wechseln zwischen Win32- und WPF-Komponenten durch Tabulatordruck
Mnemonics und Zugriffstasten, die sowohl funktionieren, wenn der Fokus in einer Win32-Komponente liegt, als auch wenn er in einer WPF-Komponente liegt.
Die klassen HwndHost und HwndSource bieten beide Implementierungen von IKeyboardInputSink, behandeln jedoch möglicherweise nicht alle gewünschten Eingabemeldungen für erweiterte Szenarien. Überschreiben Sie die entsprechenden Methoden, um das gewünschte Tastaturverhalten zu erhalten.
Die Schnittstellen bieten nur Unterstützung für das, was beim Übergang zwischen den WPF- und Win32-Bereichen passiert. Innerhalb der Win32-Region wird das Tabbingverhalten vollständig von der von Win32 implementierten Logik für Tabbing gesteuert, sofern eine solche existiert.
Siehe auch
.NET Desktop feedback