Vzájemná spolupráce grafického subsystému WPF a systému Win32

Toto téma obsahuje přehled spolupráce windows Presentation Foundation (WPF) a kódu Win32. WPF poskytuje bohaté prostředí pro vytváření aplikací. Pokud ale máte značné investice do kódu Win32, může být efektivnější použít některý z těchto kódů.

Základy spolupráce WPF a Win32

Existují dvě základní techniky pro spolupráci mezi kódem WPF a Win32.

  • Hostování obsahu WPF v okně Win32 Pomocí této techniky můžete použít pokročilé grafické funkce WPF v rámci standardního okna a aplikace Win32.

  • Hostování okna Win32 v obsahu WPF Pomocí této techniky můžete použít existující vlastní ovládací prvek Win32 v kontextu jiného obsahu WPF a předat data přes hranice.

Každá z těchto technik je koncepčně představena v tomto tématu. Další kódově orientované ilustrace hostování WPF v systému Win32 naleznete v návodu : Hostování obsahu WPF v systému Win32. Další kódově orientované ilustrace hostování Win32 ve WPF naleznete v návodu : Hostování ovládacího prvku Win32 ve WPF.

Projekty spolupráce WPF

Rozhraní API WPF jsou spravovaný kód, ale většina stávajících programů Win32 je napsaná v nespravovaném jazyce C++. Z nespravovaného programu nelze volat rozhraní API WPF. Pomocí /clr možnosti s kompilátorem Microsoft Visual C++ ale můžete vytvořit smíšený nespravovaný program, ve kterém můžete bez problémů kombinovat spravovaná a nespravovaná volání rozhraní API.

Jednou z komplikací na úrovni projektu je, že nelze kompilovat soubory XAML (Extensible Application Markup Language) do projektu C++. Existuje několik technik oddělení projektu, které by to kompenzovaly.

  • Vytvořte knihovnu DLL jazyka C#, která obsahuje všechny vaše stránky XAML jako kompilované sestavení, a pak mít spustitelný soubor C++ tento soubor DLL jako odkaz.

  • Vytvořte spustitelný soubor jazyka C# pro obsah WPF a požádejte jej o odkaz na knihovnu DLL jazyka C++, která obsahuje obsah Win32.

  • Slouží Load k načtení libovolného XAML za běhu místo kompilace XAML.

  • Nepoužívejte XAML vůbec a zapisujte všechny wpf v kódu a sestavte strom elementu z Application.

Používejte jakýkoli přístup, který vám nejlépe vyhovuje.

Poznámka:

Pokud jste C++/CLI ještě nepoužívali, můžete si všimnout některých "nových" klíčových slov, jako jsou a gcnewnullptr v příkladech kódu pro spolupráci. Tato klíčová slova nahrazují starší syntaxi dvojitého podtržítka (__gc) a poskytují přirozenější syntaxi spravovaného kódu v jazyce C++. Další informace o spravovaných funkcích C++/CLI najdete v tématu Rozšíření komponent pro platformy runtime.

Jak WPF používá Hwndy

Chcete-li, aby co nejvíce z WPF "HWND interop", musíte pochopit, jak WPF používá HWND. Pro jakýkoli HWND nemůžete kombinovat vykreslování WPF s vykreslováním DirectX nebo GDI / GDI+ vykreslování. To má řadu důsledků. Pokud chcete tyto modely vykreslování kombinovat, musíte především vytvořit řešení pro spolupráci a používat určené segmenty spolupráce pro každý model vykreslování, který se rozhodnete použít. Chování vykreslování také vytváří omezení "vzdušného prostoru" pro to, co může vaše řešení spolupráce dosáhnout. Koncept "vzdušného prostoru" je podrobněji vysvětlen v tématu Přehled technologických oblastí.

Všechny prvky WPF na obrazovce jsou nakonec podporovány HWND. Když vytváříte WPF , WPF Windowvytvoří HWND nejvyšší úrovně a používá HwndSource k vložení Window a jeho WPF obsah uvnitř HWND. Zbytek obsahu WPF ve sdílených složkách aplikace, které singulár HWND. Výjimkou jsou nabídky, rozevírací nabídky se seznamem a další automaticky otevíraná okna. Tyto prvky vytvářejí vlastní okno nejvyšší úrovně, což je důvod, proč může nabídka WPF potenciálně jít přes okraj okna HWND, který ho obsahuje. Když použijete HwndHost k vložení HWND uvnitř WPF, WPF informuje Win32, jak umístit nový podřízený HWND vzhledem k WPF Window HWND.

Související koncept HWND je transparentnost uvnitř a mezi jednotlivými HWND. Toto téma je také popsáno v tématu Přehled technologických oblastí.

Hostování obsahu WPF v okně Microsoft Win32

Klíčem k hostování WPF v okně Win32 je HwndSource třída. Tato třída zabalí obsah WPF do okna Win32, aby bylo možné obsah WPF začlenit do uživatelského rozhraní jako podřízené okno. Následující přístup kombinuje Win32 a WPF v jedné aplikaci.

  1. Implementujte obsah WPF (kořenový prvek obsahu) jako spravovanou třídu. Třída obvykle dědí z jedné z tříd, které mohou obsahovat více podřízených prvků nebo které se používají jako kořenový prvek, například DockPanel nebo Page. V dalších krocích se tato třída označuje jako třída obsahu WPF a instance třídy jsou označovány jako objekty obsahu WPF.

  2. Implementujte aplikaci pro Windows pomocí C++/CLI. Pokud začínáte s existující nespravovanou aplikací C++, můžete ji obvykle povolit volání spravovaného kódu změnou nastavení projektu tak, aby zahrnoval příznak kompilátoru /clr (úplný rozsah toho, co může být nezbytné k podpoře /clr kompilace není popsáno v tomto tématu).

  3. Nastavte model threadingu na Jednovláknový byt (STA). WPF používá tento model podprocesů.

  4. Zpracujte oznámení WM_CREATE v okně.

  5. V rámci obslužné rutiny (nebo funkce, kterou obslužná rutina volá), postupujte takto:

    1. Vytvořte nový HwndSource objekt s nadřazeným oknem HWND jako jeho parent parametrem.

    2. Vytvořte instanci třídy obsahu WPF.

    3. Přiřaďte objektu obsahu WPF odkaz na vlastnost objektu HwndSourceRootVisual .

    4. Vlastnost HwndSource objektu Handle obsahuje popisovač okna (HWND). Chcete-li získat HWND, který můžete použít v nespravované části aplikace, přetypujte Handle.ToPointer() na HWND.

  6. Implementujte spravovanou třídu, která obsahuje statické pole, které obsahuje odkaz na objekt obsahu WPF. Tato třída umožňuje získat odkaz na objekt obsahu WPF z vašeho kódu Win32, ale důležitější je, aby se zabránilo HwndSource neúmyslné uvolňování paměti.

  7. Přijímat oznámení z objektu obsahu WPF připojením obslužné rutiny k jedné nebo více událostem objektu obsahu WPF.

  8. Komunikujte s objektem obsahu WPF pomocí odkazu, který jste uložili ve statickém poli k nastavení vlastností, volání metod atd.

Poznámka:

Některé nebo všechny definice třídy obsahu WPF pro krok 1 v XAML můžete provést pomocí výchozí částečné třídy třídy obsahu, pokud vytvoříte samostatné sestavení a pak na něj odkazujete. I když obvykle zahrnete Application objekt jako součást kompilace XAML do sestavení, nebudete ho používat Application jako součást spolupráce, stačí použít jednu nebo více kořenových tříd pro soubory XAML odkazované aplikací a odkazovat na jejich částečné třídy. Zbytek postupu je v podstatě podobný výše uvedenému postupu.

Každý z těchto kroků je ilustrován kódem v tématu Návod: Hostování obsahu WPF v systému Win32.

Hostování okna Microsoft Win32 ve WPF

Klíčem k hostování okna Win32 v rámci jiného obsahu WPF je HwndHost třída. Tato třída zabalí okno do elementu WPF, který lze přidat do stromu elementů WPF. HwndHost podporuje také rozhraní API, která umožňují provádět takové úlohy, jako jsou zpracování zpráv pro hostované okno. Základní postup je:

  1. Vytvoření stromu elementů pro aplikaci WPF (může být prostřednictvím kódu nebo revize). Vyhledejte vhodný a povolený bod ve stromu prvků, kde lze implementaci HwndHost přidat jako podřízený prvek. Ve zbývající části těchto kroků se tento prvek označuje jako prvek rezervace.

  2. Odvozujte od HwndHost vytvoření objektu, který obsahuje váš obsah Win32.

  3. V této třídě hostitele přepište metodu HwndHostBuildWindowCore. Vrátí HWND hostovaného okna. Skutečné ovládací prvky můžete chtít zabalit jako podřízené okno vráceného okna; zabalení ovládacích prvků v okně hostitele poskytuje jednoduchý způsob, jak obsah WPF přijímat oznámení z ovládacích prvků. Tato technika pomáhá opravit některé problémy Win32 týkající se zpracování zpráv na hranici hostovaného ovládacího prvku.

  4. HwndHost Přepsat metody DestroyWindowCore a WndProc. Záměrem je zpracovat vyčištění a odebrat odkazy na hostovaný obsah, zejména pokud jste vytvořili odkazy na nespravované objekty.

  5. V souboru s kódem vytvořte instanci třídy hostování ovládacího prvku a nastavte ji jako podřízený prvek rezervace. Obvykle byste použili obslužnou rutinu události, například Loaded, nebo použijte konstruktor částečné třídy. Obsah spolupráce ale můžete také přidat prostřednictvím chování modulu runtime.

  6. Zpracování vybraných zpráv okna, jako jsou například řídicí oznámení. Existují dva přístupy. Oba poskytují identický přístup ke streamu zpráv, takže vaše volba je z velké části otázkou usnadnění programování.

    • Implementujte zpracování zpráv pro všechny zprávy (nejen vypnout zprávy) v přepsání HwndHost metody WndProc.

    • Hostující prvek WPF zpracovává zprávy zpracováním MessageHook události. Tato událost je vyvolána pro každou zprávu, která je odeslána do hlavního okna procedury hostovaného okna.

    • Zprávy z oken, které jsou mimo proces, nelze zpracovat pomocí WndProc.

  7. Komunikujte s hostovaným oknem pomocí volání nespravované SendMessage funkce pomocí volání platformy.

Pomocí těchto kroků vytvoříte aplikaci, která funguje se vstupem myši. Podporu tabbingu pro hostované okno můžete přidat implementací IKeyboardInputSink rozhraní.

Každý z těchto kroků je ilustrován kódem v tématu Návod: Hostování ovládacího prvku Win32 ve WPF.

Hwndy Uvnitř WPF

Můžete si představit HwndHost jako zvláštní ovládací prvek. (Technicky vzato je odvozená FrameworkElement třída, nikoli odvozená Control třída, ale dá se považovat za ovládací prvek pro účely spolupráce.) HwndHost abstrahuje základní povahu hostovaného obsahu Win32 tak, HwndHost aby zbytek WPF považoval hostovaný obsah za jiný objekt podobný řízení, který by měl vykreslit a zpracovat vstup. HwndHost obecně se chová stejně jako jakýkoli jiný WPF FrameworkElement, ačkoli existují některé důležité rozdíly kolem výstupu (kreslení a grafiky) a vstupu (myš a klávesnice) na základě omezení toho, co základní HWND může podporovat.

Důležité rozdíly ve výstupním chování

  • FrameworkElement, což je HwndHost základní třída, má poměrně málo vlastností, které naznačují změny uživatelského rozhraní. Patří mezi ně například vlastnosti FrameworkElement.FlowDirection, které mění rozložení prvků v daném prvku jako nadřazený prvek. Většina těchto vlastností však není mapována na možné ekvivalenty Win32, i když takové ekvivalenty mohou existovat. Příliš mnoho těchto vlastností a jejich významů je příliš specifické pro vykreslovací technologii, aby mapování bylo praktické. Proto nastavení vlastností, jako FlowDirection je například zapnuto HwndHost , nemá žádný vliv.

  • HwndHost nelze otočit, škálovat, zkosit nebo jinak ovlivnit transformací.

  • HwndHost nepodporuje Opacity vlastnost (alfa blending). Pokud obsah uvnitř HwndHost operací System.Drawing , které obsahují alfa informace, to není porušení, ale HwndHost jako celek podporuje pouze neprůhlednost = 1,0 (100 %).

  • HwndHost zobrazí se nad ostatními prvky WPF ve stejném okně nejvyšší úrovně. ToolTip Nebo ContextMenu vygenerovaná nabídka je však samostatné okno nejvyšší úrovně, takže se bude chovat správně s HwndHost.

  • HwndHost nerespektuje oblast výřezu nadřazeného UIElementobjektu . Toto je potenciálně problém, pokud se pokusíte umístit HwndHost třídu do oblasti posouvání nebo Canvas.

Důležité rozdíly ve vstupním chování

  • Obecně platí, že i když jsou vstupní zařízení vymezená v HwndHost hostované oblasti Win32, vstupní události přejdou přímo na Win32.

  • Zatímco myš je nad HwndHost, vaše aplikace neobdrží WPF události myši a hodnota WPF vlastnost IsMouseOver bude false.

  • HwndHost I když je fokus klávesnice, aplikace nebude přijímat události klávesnice WPF a hodnota vlastnosti IsKeyboardFocusWithin WPF bude false.

  • Když je fokus HwndHost uvnitř a změní se na jiný ovládací prvek uvnitř HwndHost, aplikace neobdrží události GotFocus WPF nebo LostFocus.

  • Související vlastnosti stylusu a události jsou analogické a neohlašujte informace, zatímco pero je konec HwndHost.

Tabbing, Mnemonics a Accelerators

IKeyboardInputSite Rozhraní IKeyboardInputSink umožňují vytvořit bezproblémové prostředí klávesnice pro smíšené aplikace WPF a Win32:

  • Tabbing between Win32 and WPF components

  • Měmonics a akcelerátory, které fungují, když je fokus v rámci komponenty Win32 a kdy je v komponentě WPF.

Obě třídy HwndHostHwndSource poskytují implementace IKeyboardInputSink, ale nemusí zpracovat všechny vstupní zprávy, které chcete pro pokročilejší scénáře. Přepište příslušné metody, abyste získali požadované chování klávesnice.

Rozhraní poskytují podporu pouze pro to, co se stane při přechodu mezi oblastmi WPF a Win32. V rámci oblasti Win32 je chování tabulátoru zcela řízeno logikou implementace Win32 pro tabbing, pokud existuje.

Viz také