WPF i Win32 — Współdziałanie

Ten temat zawiera omówienie sposobu współdziałania programu Windows Presentation Foundation (WPF) i kodu Win32. WPF zapewnia bogate środowisko do tworzenia aplikacji. Jednak jeśli masz znaczną inwestycję w kod Win32, może to być bardziej skuteczne w celu ponownego użycia niektórych kodu.

WPF i Win32 Interoperation Basics

Istnieją dwie podstawowe techniki współdziałania między kodami WPF i Win32.

  • Hostowanie zawartości WPF w oknie Win32. Dzięki tej technice można użyć zaawansowanych funkcji graficznych WPF w ramach standardowego okna i aplikacji Win32.

  • Hostowanie okna Win32 w zawartości WPF. Dzięki tej technice możesz użyć istniejącej niestandardowej kontrolki Win32 w kontekście innej zawartości WPF i przekazać dane przez granice.

Każda z tych technik jest koncepcyjnie wprowadzana w tym temacie. Aby uzyskać bardziej zorientowaną na kod ilustrację hostingu WPF w win32, zobacz Przewodnik: hosting zawartości WPF w Win32. Aby uzyskać bardziej zorientowaną na kod ilustrację hostingu Win32 w WPF, zobacz Przewodnik: hosting kontrolki Win32 w WPF.

Projekty współdziałania WPF

Interfejsy API WPF są kodem zarządzanym, ale większość istniejących programów Win32 jest zapisywana w niezarządzanym języku C++. Nie można wywoływać interfejsów API WPF z prawdziwego niezarządzanego programu. Jednak za pomocą /clr opcji w kompilatorze programu Microsoft Visual C++ można utworzyć mieszany program niezarządzany, w którym można bezproblemowo mieszać zarządzane i niezarządzane wywołania interfejsu API.

Jednym z komplikacji na poziomie projektu jest to, że nie można skompilować plików języka XAML (Extensible Application Markup Language) w projekcie C++. Istnieje kilka technik dzielenia projektów, aby to zrekompensować.

  • Utwórz bibliotekę DLL języka C#, która zawiera wszystkie strony XAML jako skompilowany zestaw, a następnie utwórz plik wykonywalny języka C++ zawierający bibliotekę DLL jako odwołanie.

  • Utwórz plik wykonywalny języka C# dla zawartości WPF i utwórz odwołanie do biblioteki DLL języka C++, która zawiera zawartość win32.

  • Użyj Load polecenia , aby załadować dowolny kod XAML w czasie wykonywania, zamiast kompilować kod XAML.

  • Nie należy w ogóle używać języka XAML i pisać cały plik WPF w kodzie, tworząc drzewo elementów z pliku Application.

Użyj dowolnego podejścia, które działa najlepiej dla Ciebie.

Uwaga

Jeśli wcześniej nie używasz języka C++/CLI, możesz zauważyć kilka "nowych" słów kluczowych, takich jak gcnew i nullptr w przykładach kodu współdziałania. Te słowa kluczowe zastępują starszą składnię podwójnego podkreślenia (__gc) i zapewniają bardziej naturalną składnię kodu zarządzanego w języku C++. Aby dowiedzieć się więcej o funkcjach zarządzanych języka C++/CLI, zobacz Rozszerzenia składników dla platform środowiska uruchomieniowego.

Jak WPF używa Hwnds

Aby jak najlepiej wykorzystać interop WPF "HWND", musisz zrozumieć, jak WPF używa HWNDs. W przypadku dowolnego HWND nie można mieszać renderowania WPF z renderowaniem DirectX lub renderowaniem GDI/ GDI+. Ma to wiele konsekwencji. Przede wszystkim, aby w ogóle mieszać te modele renderowania, należy utworzyć rozwiązanie międzyoperacyjne i użyć wyznaczonych segmentów współdziałania dla każdego modelu renderowania, którego chcesz użyć. Ponadto zachowanie renderowania powoduje ograniczenie "przestrzeni powietrznej" dla tego, co może osiągnąć rozwiązanie interoperacyjne. Pojęcie "przestrzeni powietrznej" zostało szczegółowo wyjaśnione w temacie Omówienie regionów technologii.

Wszystkie elementy WPF na ekranie są ostatecznie wspierane przez HWND. Podczas tworzenia WPF WPF program WPF Windowtworzy HWND najwyższego poziomu i używa elementu HwndSource , aby umieścić Window i jego zawartość WPF wewnątrz HWND. Pozostała część zawartości WPF w aplikacji udostępnia pojedynczą wartość HWND. Wyjątkiem są menu, listy rozwijane pól kombi i inne wyskakujące okienka. Te elementy tworzą własne okno najwyższego poziomu, dlatego menu WPF może potencjalnie przejść obok krawędzi okna HWND, który go zawiera. Gdy używasz HwndHost do umieszczenia HWND wewnątrz WPF, WPF informuje Win32, jak umieścić nowe dziecko HWND względem WPF Window HWND.

Powiązana koncepcja HWND to przezroczystość wewnątrz i między poszczególnymi elementami HWND. Omówiono to również w temacie Omówienie regionów technologii.

Hostowanie zawartości WPF w oknie Microsoft Win32

Kluczem do hostowania WPF w oknie Win32 jest HwndSource klasa . Ta klasa opakowuje zawartość WPF w oknie Win32, dzięki czemu zawartość WPF może być włączona do interfejsu użytkownika jako okno podrzędne. Poniższe podejście łączy win32 i WPF w jednej aplikacji.

  1. Zaimplementuj zawartość WPF (element główny zawartości) jako klasę zarządzaną. Zazwyczaj klasa dziedziczy z jednej z klas, które mogą zawierać wiele elementów podrzędnych i/lub używane jako element główny, taki jak DockPanel lub Page. W kolejnych krokach ta klasa jest określana jako klasa zawartości WPF, a wystąpienia klasy są określane jako obiekty zawartości WPF.

  2. Zaimplementuj aplikację systemu Windows przy użyciu języka C++/interfejsu wiersza polecenia. Jeśli zaczynasz od istniejącej niezarządzanej aplikacji języka C++, zwykle możesz włączyć wywoływanie kodu zarządzanego przez zmianę ustawień projektu w /clr celu uwzględnienia flagi kompilatora (pełny zakres elementów, które mogą być konieczne do obsługi /clr kompilacji, nie zostało opisane w tym temacie).

  3. Ustaw model wątkowania na Jednowątkowy Apartament (STA). WPF używa tego modelu wątkowania.

  4. Obsługa powiadomienia WM_CREATE w procedurze okna.

  5. W programie obsługi (lub funkcji wywoływanej przez program obsługi) wykonaj następujące czynności:

    1. Utwórz nowy HwndSource obiekt z oknem nadrzędnym HWND jako jego parent parametrem.

    2. Utwórz wystąpienie klasy zawartości WPF.

    3. Przypisz odwołanie do obiektu zawartości WPF do HwndSource właściwości obiektu RootVisual .

    4. Właściwość HwndSource object Handle zawiera uchwyt okna (HWND). Aby uzyskać HWND, którego można użyć w niezarządzanej części aplikacji, rzutuj Handle.ToPointer() na HWND.

  6. Zaimplementuj klasę zarządzaną zawierającą pole statyczne zawierające odwołanie do obiektu zawartości WPF. Ta klasa umożliwia uzyskanie odwołania do obiektu zawartości WPF z kodu Win32, ale co ważniejsze uniemożliwia HwndSource niezamierzone odzyskiwanie pamięci.

  7. Odbieranie powiadomień z obiektu zawartości WPF przez dołączenie programu obsługi do co najmniej jednego zdarzenia obiektu zawartości WPF.

  8. Komunikowanie się z obiektem zawartości WPF przy użyciu odwołania przechowywanego w polu statycznym w celu ustawienia właściwości, metod wywołania itp.

Uwaga

Możesz wykonać niektóre lub wszystkie definicje klasy zawartości WPF dla kroku 1 w języku XAML przy użyciu domyślnej częściowej klasy zawartości, jeśli utworzysz oddzielny zestaw, a następnie odwołaj się do niego. Chociaż zazwyczaj obiekt jest dołączany Application jako część kompilowania kodu XAML do zestawu, nie jest używany w Application ramach współdziałania, należy po prostu użyć jednej lub kilku klas głównych dla plików XAML, o których mowa w aplikacji i odwołać się do ich klas częściowych. Pozostała część procedury jest zasadniczo podobna do przedstawionej powyżej.

Każdy z tych kroków jest zilustrowany za pomocą kodu w temacie Przewodnik: hostowanie zawartości WPF w win32.

Hostowanie okna Microsoft Win32 w WPF

Kluczem do hostowania okna Win32 w innej zawartości WPF jest HwndHost klasa . Ta klasa opakowuje okno w elemecie WPF, który można dodać do drzewa elementów WPF. HwndHost Obsługuje również interfejsy API, które umożliwiają wykonywanie takich zadań, jak przetwarzanie komunikatów dla okna hostowanego. Podstawowa procedura to:

  1. Utwórz drzewo elementów dla aplikacji WPF (może to być kod lub znacznik). Znajdź odpowiedni i dopuszczalny punkt w drzewie elementów, w którym HwndHost można dodać implementację jako element podrzędny. W pozostałej części tych kroków ten element jest określany jako element rezerwowania.

  2. HwndHost Utwórz obiekt, który przechowuje zawartość Win32.

  3. W tej klasie hostów przesłonięć metodę HwndHostBuildWindowCore. Zwróć wartość HWND okna hostowanego. Możesz chcieć opakowować rzeczywiste kontrolki jako okno podrzędne zwróconego okna; Zawijanie kontrolek w oknie hosta zapewnia prosty sposób odbierania powiadomień z kontrolek przez zawartość WPF. Ta technika pomaga rozwiązać niektóre problemy z systemem Win32 dotyczące obsługi komunikatów na granicy hostowanej kontrolki.

  4. Zastąpij HwndHost metody DestroyWindowCore i WndProc. Celem jest przetwarzanie oczyszczania i usuwania odwołań do hostowanej zawartości, szczególnie w przypadku utworzenia odwołań do niezarządzanych obiektów.

  5. W pliku za pomocą kodu utwórz wystąpienie klasy hostingu kontrolek i utwórz je jako element podrzędny elementu rezerwowania. Zazwyczaj należy użyć procedury obsługi zdarzeń, takiej jak Loaded, lub użyć konstruktora klasy częściowej. Można jednak również dodać zawartość międzyoperacyjną za pomocą zachowania środowiska uruchomieniowego.

  6. Przetwarzanie wybranych komunikatów okna, takich jak powiadomienia sterujące. Istnieją dwa podejścia. Obie zapewniają identyczny dostęp do strumienia komunikatów, więc wybór jest w dużej mierze kwestią wygody programistycznej.

    • Zaimplementuj przetwarzanie komunikatów dla wszystkich komunikatów (nie tylko zamykanie HwndHost komunikatów) w zastąpieniu metody WndProc.

    • Hostowanie elementu WPF przetwarza komunikaty przez obsługę MessageHook zdarzenia. To zdarzenie jest zgłaszane dla każdego komunikatu wysyłanego do procedury okna głównego hostowanego okna.

    • Nie można przetwarzać komunikatów z okien, które są poza procesem przy użyciu polecenia WndProc.

  7. Komunikowanie się z hostowanym oknem przy użyciu wywołania platformy w celu wywołania funkcji niezarządzanej SendMessage .

Wykonanie tych kroków powoduje utworzenie aplikacji, która działa z danymi wejściowymi myszy. Obsługę tabulacji dla okna hostowanego można dodać, implementując IKeyboardInputSink interfejs.

Każdy z tych kroków jest zilustrowany za pomocą kodu w temacie Przewodnik: hostowanie kontrolki Win32 w WPF.

Hwnds wewnątrz WPF

Możesz traktować HwndHost jako specjalną kontrolkę. (Technicznie, HwndHost jest klasą pochodną FrameworkElement , a nie klasą pochodną Control , ale można ją uznać za kontrolkę na potrzeby współdziałania).) HwndHost tworzy abstrakcję bazowego charakteru win32 hostowanej zawartości, tak aby pozostała część zawartości WPF była innym obiektem przypominającym kontrolkę, który powinien renderować i przetwarzać dane wejściowe. HwndHost zwykle zachowuje się jak każdy inny WPF FrameworkElement, chociaż istnieją pewne istotne różnice w zakresie danych wyjściowych (rysunek i grafika) i wprowadzania (myszy i klawiatury) na podstawie ograniczeń, jakie mogą obsługiwać bazowe elementy HWND.

Istotne różnice w zachowaniu danych wyjściowych

  • FrameworkElement, która jest klasą HwndHost bazową, ma sporo właściwości, które sugerują zmiany w interfejsie użytkownika. Obejmują one właściwości, takie jak FrameworkElement.FlowDirection, które zmieniają układ elementów w tym elemecie jako element nadrzędny. Jednak większość tych właściwości nie jest mapowana na możliwe odpowiedniki Win32, nawet jeśli takie odpowiedniki mogą istnieć. Zbyt wiele z tych właściwości i ich znaczenia są zbyt specyficzne dla technologii renderowania, aby mapowania były praktyczne. W związku z tym ustawienie właściwości, takich jak FlowDirection na, HwndHost nie ma wpływu.

  • HwndHost Nie można obracać, skalować, niesymetrycznie ani w inny sposób wpływać na przekształcenie.

  • HwndHost nie obsługuje Opacity właściwości (mieszanie alfa). Jeśli zawartość wewnątrz HwndHost wykonuje operacje System.Drawing zawierające informacje alfa, to samo nie jest naruszeniem, ale HwndHost jako całość obsługuje tylko nieprzezroczystość = 1,0 (100%).

  • HwndHost pojawi się na szczycie innych elementów WPF w tym samym oknie najwyższego poziomu. ToolTip Jednak menu lub ContextMenu wygenerowane jest osobnym oknem najwyższego poziomu, dlatego będzie działać poprawnie za pomocą polecenia HwndHost.

  • HwndHost nie uwzględnia obszaru wycinków elementu nadrzędnego UIElement. Jest to potencjalnie problem, jeśli próbujesz umieścić klasę HwndHost w regionie przewijania lub Canvas.

Istotne różnice w zachowaniu danych wejściowych

  • Ogólnie rzecz biorąc, podczas gdy urządzenia wejściowe są objęte zakresem w regionie hostowanego HwndHost systemu Win32, zdarzenia wejściowe przechodzą bezpośrednio do win32.

  • Gdy mysz znajduje HwndHostsię na obiekcie , aplikacja nie odbiera zdarzeń myszy WPF, a wartość właściwości IsMouseOver WPF będzie równa false.

  • Chociaż obiekt HwndHost ma fokus klawiatury, aplikacja nie będzie otrzymywać zdarzeń klawiatury WPF, a wartość właściwości IsKeyboardFocusWithin WPF będzie równa false.

  • Gdy fokus znajduje się w obiekcie HwndHost i zmienia się na inną kontrolkę wewnątrz HwndHost, aplikacja nie otrzyma zdarzeń GotFocus WPF ani LostFocus.

  • Powiązane właściwości i zdarzenia stylu są analogiczne i nie zgłaszaj informacji, podczas gdy rysik kończy się .HwndHost

Tabbing, Mnemonics i Akceleratory

Interfejsy IKeyboardInputSink i IKeyboardInputSite umożliwiają tworzenie bezproblemowego środowiska klawiatury dla mieszanych aplikacji WPF i Win32:

  • Tabbing między składnikami Win32 i WPF

  • Mnemonics i akceleratory, które działają zarówno wtedy, gdy fokus znajduje się w składniku Win32, jak i w składniku WPF.

Klasy HwndHost i HwndSource zapewniają implementacje programu , ale mogą nie obsługiwać wszystkich komunikatów IKeyboardInputSinkwejściowych, które mają być przeznaczone dla bardziej zaawansowanych scenariuszy. Zastąp odpowiednie metody, aby uzyskać odpowiednie zachowanie klawiatury.

Interfejsy zapewniają obsługę tylko tego, co dzieje się w przypadku przejścia między regionami WPF i Win32. W regionie Win32 zachowanie tabulacji jest całkowicie kontrolowane przez zaimplementowaną logikę win32 na potrzeby tabulacji, jeśli istnieje.

Zobacz też