Megosztás a következőn keresztül:


WPF és Win32 együttműködés

Ez a témakör áttekintést nyújt a Windows Presentation Foundation (WPF) és a Win32 kód együttműködéséről. A WPF gazdag környezetet biztosít az alkalmazások létrehozásához. Ha azonban jelentős befektetéssel rendelkezik a Win32-kódba, hatékonyabb lehet a kód egy részének újrafelhasználása.

A WPF és a Win32 együttműködésének alapjai

A WPF és a Win32 kód közötti együttműködésnek két alapvető technikája van.

  • WPF-tartalom elhelyezése egy Win32-ablakban. Ezzel a technikával a WPF fejlett grafikus képességeit használhatja egy szabványos Win32-ablak és alkalmazás keretein belül.

  • Win32-ablak beágyazása WPF-tartalomba. Ezzel a technikával egy meglévő egyéni Win32-vezérlőt használhat más WPF-tartalmak kontextusában, és adatokat továbbíthat a határokon.

Ezen technikák mindegyike fogalmilag be van vezetve ebben a témakörben. A WPF Win32-ben való üzemeltetésének kódorientáltabb illusztrációját a útmutatóban találja: WPF-tartalom üzemeltetése a Win32-ben. A Win32 WPF-ben való üzemeltetésének kódorientáltabb szemléltetéséért lásd útmutatót: Win32-vezérlő üzemeltetése a WPF.

WPF együttműködési projektek

A WPF API-k felügyelt kódnak számítanak, de a legtöbb meglévő Win32-program nem felügyelt C++ nyelven van megírva. A WPF API-kat nem hívhatja meg valódi, nem felügyelt programból. Ha azonban a /clr lehetőséget használja a Microsoft Visual C++ fordítóval, létrehozhat egy vegyes, felügyelt és nem felügyelt programot, ahol zökkenőmentesen keverheti a felügyelt és a nem felügyelt API-hívásokat.

Az egyik projektszintű bonyodalom az, hogy nem fordíthat XAML-fájlokat C++ projektbe. Ezt több projektmegosztási módszer is kompenzálja.

  • Hozzon létre egy C# DLL-t, amely lefordított szerelvényként tartalmazza az összes XAML-lapot, majd a C++ végrehajtható fájl hivatkozásként tartalmazza a DLL-t.

  • Hozzon létre egy C#-végrehajtható fájlt a WPF-tartalomhoz, és hivatkozzon rá egy C++ DLL-re, amely tartalmazza a Win32-tartalmat.

  • Az XAML összeállítása helyett használja a Load-t az XAML futásidőben történő betöltéséhez.

  • Ne használja egyáltalán az XAML-t, és írja meg az összes WPF-et kódban, majd állítsa össze az elemfát Application-tól kezdve.

Használjon bármilyen módszert, amely a legjobban megfelel Önnek.

Megjegyzés:

Ha korábban még nem használta a C++/CLI-t, előfordulhat, hogy néhány "új" kulcsszót, például gcnew és nullptr észlel az interoperációs kód példáiban. Ezek a kulcsszavak felülírják a régebbi dupla aláhúzásos szintaxist (__gc), és természetesebb szintaxist biztosítanak a C++-ban felügyelt kódhoz. A C++/CLI által felügyelt funkciókkal kapcsolatos további információkért tekintse meg futtatókörnyezeti platformok összetevőbővítményeit.

Hogyan használja a WPF a Hwnds-t?

A WPF "HWND interop" kihasználása érdekében tisztában kell lenni azzal, hogy a WPF hogyan használja a HWND-ket. A HWND-k esetében a WPF-renderelés nem keverhető DirectX-rendereléssel vagy GDI/GDI+ rendereléssel. Ennek számos következménye van. Elsősorban azért, hogy ezeket a renderelési modelleket egyáltalán össze lehessen keverni, létre kell hoznia egy interoperációs megoldást, és minden használni kívánt renderelési modellhez használnia kell az interoperáció kijelölt szegmenseit. A renderelési viselkedés emellett egy "légtérre" vonatkozó korlátozást is létrehoz arra vonatkozóan, amit az interoperációs megoldás el tud érni. A "légtér" fogalmát részletesebben Technológiai régiók áttekintésecímű témakörben ismertetik.

A képernyőn található összes WPF-elem végső soron egy HWND által van támogatva. WPF-Windowlétrehozásakor a WPF létrehoz egy legfelső szintű HWND-t, és egy HwndSource használatával helyezi el a Window és annak WPF-tartalmát a HWND-ben. Az alkalmazás többi WPF-tartalma ugyanazt az HWND-t használja. Kivételt képeznek a menük, a kombinált lista legördülő menüi és más előugró ablakok. Ezek az elemek létrehozzák a saját legfelső szintű ablakukat, ezért a WPF-menük esetleg túlléphetnek az azt tartalmazó HWND ablak szélén. Ha a HwndHost segítségével egy HWND-t helyez el a WPF-ben, a WPF tájékoztatja a Win32-t arról, hogyan helyezze el az új gyermek HWND-t a WPF Window HWND-hez képest.

A HWND-hez kapcsolódó fogalom az átláthatóság az egyes HWND-k között és azok között. Ezt a témakört Technológiai régiók áttekintésecímű témakörben is ismertetjük.

WPF-tartalom üzemeltetése Microsoft Win32-ablakban

A WPF Win32-ablakban való üzemeltetésének kulcsa a HwndSource osztály. Ez az osztály egy Win32-ablakban burkolja a WPF-tartalmat, így a WPF-tartalom gyermekablakként beépíthető a felhasználói felületbe. Az alábbi megközelítés egyetlen alkalmazásban egyesíti a Win32 és a WPF protokollt.

  1. Implementálja a WPF-tartalmat (a tartalom gyökérelemét) felügyelt osztályként. Az osztály általában az egyik olyan osztálytól öröklődik, amely több gyermekelemet tartalmazhat, és/vagy gyökérelemként használható, például DockPanel vagy Page. A következő lépésekben ezt az osztályt WPF-tartalomosztálynak nevezzük, az osztály példányait pedig WPF-tartalomobjektumoknak nevezzük.

  2. Windows-alkalmazás implementálása C++/CLI használatával. Ha egy meglévő, nem felügyelt C++ alkalmazással kezd, általában engedélyezheti a felügyelt kód meghívását úgy, hogy a projektbeállításokat úgy módosítja, hogy az tartalmazza a /clr fordító jelölőt (a jelen témakörben nem ismertetjük, hogy mi szükséges /clr fordítás támogatásához).

  3. Állítsa a szálmodellt egyszálas lakásra (STA). A WPF ezt a szálmodellt használja.

  4. A WM_CREATE értesítés kezelése az ablakos eljárásban.

  5. A kezelőn belül (vagy egy függvényen belül, amelyet a kezelő hív) tegye a következőket:

    1. Hozzon létre egy új HwndSource objektumot a HWND szülőablakmal parent paraméterként.

    2. Hozzon létre egy példányt a WPF tartalom osztályból.

    3. A WPF-tartalomobjektumra mutató hivatkozás hozzárendelése az HwndSource objektum RootVisual tulajdonsághoz.

    4. Az HwndSource objektum Handle tulajdonsága tartalmazza az ablakkezelői fogantyút (HWND). Ha szeretne egy HWND-t, amelyet az alkalmazás nem felügyelt részében használhat, alakítsa át a Handle.ToPointer()-t HWND-vé.

  6. Olyan felügyelt osztály implementálása, amely egy statikus mezőt tartalmaz, amely a WPF-tartalomobjektumra mutató hivatkozást tartalmaz. Ez az osztály lehetővé teszi, hogy hivatkozzon a WPF-tartalomobjektumra a Win32-kódból, de ami még fontosabb, megakadályozza, hogy a HwndSource véletlenül szemetet gyűjtsön.

  7. Értesítések fogadása a WPF tartalomobjektumból egy kezelő egy vagy több WPF-tartalomobjektum-eseményhez való csatolásával.

  8. Kommunikáljon a WPF tartalomobjektummal a statikus mezőben tárolt hivatkozással tulajdonságok, hívási módszerek stb. beállításához.

Megjegyzés:

Ha külön könyvtárat hoz létre és hivatkozik rá, akkor az első lépéshez tartozó WPF-tartalomosztály definíciójának egy részét vagy egészét elvégezheti XAML-ben a tartalomosztály alapértelmezett részleges osztályának használatával. Bár általában egy Application objektumot is beilleszt az XAML összeállításakor egy modulba, valójában nem használja a Application-et az interoperáció részeként. Csak egy vagy több gyökér osztályt használ az alkalmazás által hivatkozott XAML-fájlokhoz, és részleges osztályaikra hivatkozik. Az eljárás hátralevő része lényegében a fent vázolthoz hasonló.

Ezeket a lépéseket a következő témakörben található kód szemlélteti, Útmutató: WPF-tartalom üzemeltetése a Win32-ben.

Microsoft Win32-ablak üzemeltetése a WPF-ben

A Win32-ablak más WPF-tartalmakon belüli üzemeltetésének kulcsa a HwndHost osztály. Ez az osztály egy WPF-elembe burkolja az ablakot, amely hozzáadható egy WPF-elemfához. HwndHost olyan API-kat is támogat, amelyek lehetővé teszik az olyan feladatok elvégzését, mint az üzenetek feldolgozása az üzemeltetett ablakhoz. Az alapművelet a következő:

  1. Elemfát hozhat létre egy WPF-alkalmazáshoz (kód vagy jelölőnyelv használatával). Keressen egy megfelelő és megengedett pontot az elemfában, ahol a HwndHost implementáció gyermekelemként adható hozzá. A lépések hátralevő részében ezt az elemet tartalékelemnek nevezzük.

  2. A HwndHost alapján hozzon létre egy, a Win32-tartalmat tartalmazó objektumot.

  3. Ebben az osztályban írja felül a HwndHost metódust BuildWindowCore. Adja vissza a üzemeltetett ablak HWND azonosítóját. Előfordulhat, hogy a tényleges vezérlőelem(ek)et a visszaadott ablak gyermekablakaként szeretné becsomagolni; A vezérlők gazdaablakban való körbefuttatása egyszerű módot biztosít a WPF-tartalom számára, hogy értesítéseket kapjon a vezérlőktől. Ez a technika segít kijavítani néhány Win32-problémát az üzemeltetett vezérlőhatáron lévő üzenetkezeléssel kapcsolatban.

  4. A HwndHost metódusok DestroyWindowCore és WndProcfelülírása. A cél itt az üzemeltetett tartalomra mutató hivatkozások feldolgozása és eltávolítása, különösen akkor, ha nem felügyelt objektumokra mutató hivatkozásokat hozott létre.

  5. A kód mögötti fájlban hozza létre a vezérlő hosztoló osztály egy példányát, és tegye azt a fenntartó elem gyermekévé. Általában egy eseménykezelőt, például Loaded, vagy a részleges osztálykonstruktort használná. Az együttműködési tartalmat azonban futtatókörnyezeti viselkedéssel is hozzáadhatja.

  6. A kijelölt ablaküzenetek, például a vezérlőértesítések feldolgozása. Két megközelítés létezik. Mindkettő azonos hozzáférést biztosít az üzenetstreamhez, így a választás nagyrészt a programozási kényelem kérdése.

    • A HwndHost metódus WndProcfelülbírálásával implementálhatja az összes üzenet üzenetének feldolgozását (nem csak leállítási üzeneteket).

    • A WPF gazdaelem az MessageHook esemény kezelésével dolgozza fel az üzeneteket. Ez az esemény minden olyan üzenetnél kiváltódik, amelyet a rendszer az üzemeltetett ablak főablak-eljárásának küld.

    • A WndProchasználatával nem lehet feldolgozni a folyamaton kívül lévő windowsos üzeneteket.

  7. A nem felügyelt SendMessage függvény meghívásához platformhívással kommunikáljon a üzemeltetett ablakkal.

Az alábbi lépések végrehajtásával létrehoz egy alkalmazást, amely az egér bemenetével működik. A IKeyboardInputSink felület implementálásával többlapos támogatást adhat a üzemeltetett ablakhoz.

Minden egyes lépést kódszemléltetéssel mutatjuk be a Téma : Útmutató a Win32-vezérlő befogadására a WPF-ben című részben.

Hwnds Inside WPF

A HwndHost speciális vezérlőként is felfogható. (Technikailag a HwndHost egy FrameworkElement származtatott osztály, nem pedig Control származtatott osztály, de az együttműködés szempontjából vezérlőnek tekinthető.) HwndHost az üzemeltetett tartalom mögöttes Win32-jellegét úgy absztrakciója, hogy a WPF fennmaradó része egy másik vezérlőszerű objektumnak tekinti az üzemeltetett tartalmat, amelynek renderelnie és feldolgoznia kell a bemenetet. HwndHost általában úgy viselkedik, mint bármely más WPF-FrameworkElement, bár a kimenet (rajz és grafika) és a bemenet (egér és billentyűzet) között vannak néhány fontos különbség a mögöttes HWND-k által támogatott korlátozások alapján.

A kimeneti viselkedés jelentős eltérései

  • FrameworkElement, amely az HwndHost alaposztály, számos olyan tulajdonsággal rendelkezik, amelyek a felhasználói felület módosítását jelentik. Ezek közé tartoznak az olyan tulajdonságok, mint a FrameworkElement.FlowDirection, amelyek szülőként módosítják az elem elemeinek elrendezését. Ezeknek a tulajdonságoknak a többsége azonban nem felel meg a lehetséges Win32-ekvivalenseknek, még akkor sem, ha léteznek ilyen egyenértékűek. Túl sok ilyen tulajdonság és azok jelentése túlzottan a renderelési technológiára specializált ahhoz, hogy a leképezések gyakorlatiasak legyenek. Ezért az olyan tulajdonságok beállítása, mint amilyen a FlowDirection a HwndHost-en, nincs hatással rá.

  • HwndHost nem forgatható, nem skálázható, nem nyújtható vagy más módon nem módosítható egy transzformáció által.

  • HwndHost nem támogatja a Opacity tulajdonságot (alfa-keverés). Ha a HwndHost tartalom alfainformációt tartalmazó System.Drawing műveleteket hajt végre, az önmagában nem szabálysértés, de a HwndHost egésze csak az Átlátszatlanságot támogatja = 1,0 (100%).

  • HwndHost megjelennek a többi WPF-elem tetején ugyanabban a felső szintű ablakban. A ToolTip vagy ContextMenu által létrehozott menü egy külön felső szintű ablak, és így helyesen fog viselkedni HwndHost-vel.

  • HwndHost nem veszi figyelembe a szülő UIElementkivágási régióját. Ez akkor lehet probléma, ha egy HwndHost osztályt próbál görgethető régióba vagy a(z) Canvas-be helyezni.

A bemeneti viselkedés jelentős eltérései

  • Általánosságban elmondható, hogy míg a bemeneti eszközök hatóköre a HwndHost üzemeltetett Win32-régión belül van, a bemeneti események közvetlenül a Win32-be kerülnek.

  • Amíg az egér a HwndHostfelett van, az alkalmazás nem fogad WPF egéreseményeket, és a WPF tulajdonság IsMouseOver értéke falselesz.

  • Amíg a HwndHost rendelkezik billentyűzetfókusszal, az alkalmazás nem fogad WPF billentyű-eseményeket, és a WPF tulajdonság IsKeyboardFocusWithin értéke falselesz.

  • Ha a fókusz a HwndHost-n belül van, és egy másik vezérlőre vált a HwndHost-en belül, az alkalmazás nem kapja meg a WPF-eseményeket GotFocus vagy LostFocus.

  • A kapcsolódó tolltulajdonságok és események hasonlóak, és nem jeleznek információt, amíg a toll HwndHostfölött van.

Tabulálás, Mnemonics és Gyorsbillentyűk

A IKeyboardInputSink és IKeyboardInputSite felületek lehetővé teszik, hogy zökkenőmentes billentyűzetfelületet hozzon létre vegyes WPF- és Win32-alkalmazásokhoz:

  • A Win32 és a WPF-összetevők közötti lapozás

  • Mnemonikák és gyorsítók, amelyek akkor is működnek, ha a fókusz egy Win32-összetevőn belül van, és ha egy WPF-összetevőn belül van.

A HwndHost és a HwndSource osztály egyaránt biztosítja a IKeyboardInputSinkimplementációit, de előfordulhat, hogy nem kezelik az összes olyan bemeneti üzenetet, amelyet speciálisabb forgatókönyvekhez szeretne használni. Írd felül a megfelelő metódusokat a kívánt billentyűzet viselkedésének eléréséhez.

A felületek csak azt támogatják, ami a WPF és a Win32 régió közötti átmenet során történik. A Win32-régióban a lapozási viselkedést teljes mértékben a Win32 által implementált, a lapozáshoz használt logika vezérli, ha van ilyen.

Lásd még