WPF och Win32 Interoperation
Det här avsnittet innehåller en översikt över hur du samverkar med Windows Presentation Foundation (WPF) och Win32-kod. WPF ger en omfattande miljö för att skapa program. Men när du har en betydande investering i Win32-kod kan det vara mer effektivt att återanvända en del av koden.
Grunderna för WPF och Win32 Interoperation
Det finns två grundläggande tekniker för samverkan mellan WPF och Win32-kod.
Värd för WPF-innehåll i ett Win32-fönster. Med den här tekniken kan du använda de avancerade grafikfunktionerna i WPF inom ramen för ett win32-standardfönster och -program.
Inbädda ett Win32-fönster i WPF-innehåll. Med den här tekniken kan du använda en befintlig anpassad Win32-kontroll i kontexten för annat WPF-innehåll och skicka data över gränserna.
Var och en av dessa tekniker introduceras konceptuellt i det här avsnittet. En mer kodorienterad illustration av att vara värd för WPF i Win32 finns i Genomgång: Värd för WPF-innehåll i Win32. En mer kodorienterad illustration av att integrera Win32 i WPF finns i Genomgång: Integrera en Win32-kontroll i WPF.
WPF-interoperationsprojekt
WPF-API:er är hanterad kod, men de flesta befintliga Win32-program skrivs i ohanterad C++. Du kan inte anropa WPF-API:er från ett verkligt ohanterat program. Men genom att använda /clr
med Microsoft Visual C++-kompilatorn kan du skapa ett blandat hanterat ohanterat program där du sömlöst kan blanda hanterade och ohanterade API-anrop.
En komplikation på projektnivå är att du inte kan kompilera XAML-filer (Extensible Application Markup Language) till ett C++-projekt. Det finns flera olika tekniker för projektdivision för att kompensera för detta.
Skapa en C#-DLL som innehåller alla dina XAML-sidor som en kompilerad sammansättning och låt sedan den körbara C++-filen innehålla DLL:en som referens.
Skapa en C#-körbar fil för WPF-innehållet och låt den referera till en C++ DLL som innehåller Win32-innehållet.
Använd Load för att läsa in XAML under körning i stället för att kompilera XAML.
Använd inte XAML alls och skriv all din WPF i kod och skapa elementträdet från Application.
Använd den metod som passar dig bäst.
Not
Om du inte har använt C++/CLI tidigare kanske du märker några "nya" nyckelord, till exempel gcnew
och nullptr
i kodexemplen för interoperation. Dessa nyckelord ersätter den äldre syntaxen med dubbla understreck (__gc
) och ger en mer naturlig syntax för hanterad kod i C++. Mer information om de C++/CLI-hanterade funktionerna finns i Component Extensions for Runtime Platforms.
Så använder WPF Hwnds
För att få ut mesta möjliga av WPF "HWND interop" måste du förstå hur WPF använder HWNDs. För alla HWND kan du inte blanda WPF-rendering med DirectX-rendering eller GDI/GDI+-återgivning. Detta har ett antal konsekvenser. För att kunna blanda dessa återgivningsmodeller över huvud taget måste du skapa en interoperationslösning och använda avsedda segment av interoperation för varje återgivningsmodell som du väljer att använda. Dessutom skapar återgivningsbeteendet en "luftrumsbegränsning" för vad din interoperationslösning kan åstadkomma. Begreppet "luftrum" förklaras mer detaljerat i ämnet "Översikt över teknologiregioner" .
Alla WPF-element på skärmen backas slutligen upp av en HWND. När du skapar en WPF-Windowskapar WPF en HWND på toppnivå och använder en HwndSource för att placera Window och dess WPF-innehåll i HWND. Resten av WPF-innehållet i programmet delar det unika HWND:et. Ett undantag är menyer, nedrullningsbara listor och andra popup-fönster. Dessa element skapar ett eget fönster på den översta nivån, vilket är anledningen till att en WPF-meny potentiellt kan gå förbi kanten av fönstret HWND som innehåller den. När du använder HwndHost för att placera en HWND i WPF informerar WPF Win32 om hur du placerar den nya underordnade HWND:n i förhållande till WPF-Window HWND.
Ett relaterat begrepp till HWND är transparens inom och mellan varje HWND. Detta beskrivs också i avsnittet Technology Regions Overview.
Att hantera WPF-innehåll i ett Microsoft Win32-fönster
Nyckeln till att vara värd för en WPF i ett Win32-fönster är klassen HwndSource. Den här klassen omsluter WPF-innehållet i ett Win32-fönster så att WPF-innehållet kan införlivas i användargränssnittet som ett underordnat fönster. Följande metod kombinerar Win32 och WPF i ett enda program.
Implementera ditt WPF-innehåll (innehållsrotelementet) som en hanterad klass. Vanligtvis ärver klassen från en av klasserna som kan innehålla flera underordnade element och/eller som används som ett rotelement, till exempel DockPanel eller Page. I efterföljande steg kallas den här klassen för WPF-innehållsklassen, och instanser av klassen kallas WPF-innehållsobjekt.
Implementera ett Windows-program med C++/CLI. Om du börjar med ett befintligt ohanterat C++-program kan du vanligtvis låta det anropa hanterad kod genom att ändra projektinställningarna så att de innehåller flaggan
/clr
kompilator (det fullständiga omfånget för vad som kan vara nödvändigt för att stödja/clr
kompilering beskrivs inte i det här avsnittet).Ange trådhanteringsmodellen till Single Threaded Apartment (STA). WPF använder den här trådmodellen.
Hantera WM_CREATE-meddelandet i fönsterproceduren.
I hanteraren (eller en funktion som hanteraren anropar) gör du följande:
Skapa ett nytt HwndSource-objekt med det överordnade fönstrets HWND som dess
parent
-parameter.Skapa en instans av WPF-innehållsklassen.
Tilldela en referens till WPF-innehållsobjektet till egenskapen HwndSource objekt RootVisual.
Egenskapen HwndSource objekt Handle innehåller fönsterhandtaget (HWND). Om du vill hämta en HWND som du kan använda i den ohanterade delen av ditt program kan du casta
Handle.ToPointer()
till en HWND.
Implementera en hanterad klass som innehåller ett statiskt fält som innehåller en referens till WPF-innehållsobjektet. Med den här klassen kan du hämta en referens till WPF-innehållsobjektet från din Win32-kod, men särskilt viktigt är att den förhindrar att dina HwndSource oavsiktligt avlägsnas av skräpinsamlingen.
Ta emot meddelanden från WPF-innehållsobjektet genom att koppla en hanterare till en eller flera wpf-innehållsobjekthändelser.
Kommunicera med WPF-innehållsobjektet med hjälp av referensen som du lagrade i det statiska fältet för att ange egenskaper, anropsmetoder osv.
Not
Du kan göra en del av eller hela WPF-innehållsklassdefinitionen för steg ett i XAML med hjälp av den partiella standardklassen för innehållsklassen, om du skapar en separat sammansättning och sedan refererar till den. Även om du vanligtvis inkluderar ett Application objekt som en del av kompileringen av XAML till en sammansättning, använder du inte den Application som en del av interoperationen, men du använder bara en eller flera av rotklasserna för XAML-filer som refereras till av programmet och refererar till deras partiella klasser. Resten av förfarandet liknar i stort sett det som beskrivs ovan.
Var och en av dessa steg illustreras med kod i Walkthrough: Värd för WPF-innehåll i Win32.
Att vara värd för ett Microsoft Win32-fönster i WPF
Nyckeln till att vara värd för ett Win32-fönster i annat WPF-innehåll är klassen HwndHost. Den här klassen omsluter fönstret i ett WPF-element som kan läggas till i ett WPF-elementträd. HwndHost har också stöd för API:er som gör att du kan utföra sådana uppgifter som att bearbeta meddelanden för det värdbaserade fönstret. Den grundläggande proceduren är:
Skapa ett elementträd för ett WPF-program (kan vara via kod eller markering). Hitta en lämplig och tillåten punkt i elementträdet där den HwndHost implementeringen kan läggas till som ett underordnat element. I resten av dessa steg kallas det här elementet för reservelementet.
Härled från HwndHost för att skapa ett objekt som innehåller ditt Win32-innehåll.
I den värdklassen åsidosätter du metoden HwndHostBuildWindowCore. Returnera HWND för det värdbaserade fönstret. Du kanske vill omsluta de faktiska kontrollerna som ett underordnat fönster i det returnerade fönstret. Att omsluta kontrollerna i ett värdfönster är ett enkelt sätt för WPF-innehållet att ta emot meddelanden från kontrollerna. Den här tekniken hjälper till att korrigera för vissa Win32-problem med meddelandehantering vid den värdbaserade kontrollgränsen.
Åsidosätt de HwndHost metoderna DestroyWindowCore och WndProc. Avsikten här är att bearbeta rensning och ta bort referenser till det värdbaserade innehållet, särskilt om du har skapat referenser till ohanterade objekt.
I din code-behind-fil skapar du en instans av klassen för kontrollvärden och gör den till ett barn av reservelementet. Vanligtvis använder du en händelsehanterare som Loadedeller använder den partiella klasskonstruktorn. Men du kan också lägga till interoperabilitetsinnehåll via ett körningstidbeteende.
Bearbeta markerade fönstermeddelanden, till exempel kontrollmeddelanden. Det finns två metoder. Båda ger identisk åtkomst till meddelandeströmmen, så ditt val handlar till stor del om programmerings bekvämlighet.
Implementera meddelandebearbetning för alla meddelanden (inte bara nedstängningsmeddelanden) i din åsidosättning av HwndHost-metoden WndProc.
Låt WPF-elementet bearbeta meddelandena genom att hantera händelsen MessageHook. Den här händelsen utlöses för varje meddelande som skickas till huvudfönsterproceduren i det värdbaserade fönstret.
Du kan inte bearbeta meddelanden från windows som är ur process med hjälp av WndProc.
Kommunicera med det värdbaserade fönstret med hjälp av plattformsanrop för att anropa den ohanterade
SendMessage
funktionen.
Genom att följa dessa steg skapas en applikation som fungerar med musindata. Du kan lägga till stöd för tabbning för ditt värdbaserade fönster genom att implementera IKeyboardInputSink-gränssnittet.
Vars och ett av dessa steg illustreras med kod i ämnet Steg-för-steg-guide: Värd för en Win32-kontroll i WPF.
Hwnds Inuti WPF
Du kan se HwndHost som en särskild kontroll. (Tekniskt sett är HwndHost en FrameworkElement härledd klass, inte en Control härledd klass, men den kan betraktas som en kontroll i samband med interoperation.) HwndHost abstraherar den underliggande Win32-karaktären för det värdbaserade innehållet så att resten av WPF anser att det värdbaserade innehållet är ett annat kontrollliknande objekt, vilket bör återge och bearbeta indata. HwndHost beter sig vanligtvis som andra WPF-FrameworkElement, även om det finns några viktiga skillnader kring utdata (ritning och grafik) och indata (mus och tangentbord) baserat på begränsningar för vad de underliggande HWND:erna kan stödja.
Anmärkningsvärda skillnader i utdatabeteende
FrameworkElement, som är den HwndHost basklassen, har en hel del egenskaper som innebär ändringar i användargränssnittet. Dessa inkluderar egenskaper som FrameworkElement.FlowDirection, vilket ändrar layouten för element inom det elementet som agerar som en förälder. De flesta av dessa egenskaper är dock inte mappade till möjliga Win32-motsvarigheter, även om sådana motsvarigheter kan finnas. För många av dessa egenskaper och deras betydelser är för återgivningsteknikspecifika för att mappningar ska vara praktiska. Därför har det ingen effekt att ange egenskaper som FlowDirection på HwndHost.
HwndHost kan inte roteras, skalas, skevas eller påverkas på annat sätt av en transformering.
HwndHost stöder inte egenskapen Opacity (alfablandning). Om innehållet i HwndHost utför System.Drawing åtgärder som innehåller alfainformation, är det i sig inte en överträdelse, men HwndHost som helhet stöder endast Opacitet = 1,0 (100%).
HwndHost visas ovanpå andra WPF-element i samma fönster på den översta nivån. En ToolTip- eller ContextMenu genererad meny är dock ett separat fönster på den översta nivån och fungerar därför korrekt med HwndHost.
HwndHost respekterar inte urklippsregionen för dess överordnade UIElement. Det här är potentiellt ett problem om du försöker placera en HwndHost-klass i en rullningsregion eller Canvas.
Anmärkningsvärda skillnader i indatabeteende
I allmänhet, medan indataenheter är begränsade inom HwndHost värdbaserad Win32-region, går indatahändelser direkt till Win32.
Medan musen är över HwndHosttar programmet inte emot WPF-mushändelser och värdet för WPF-egenskapen IsMouseOver blir
false
.Även om HwndHost har tangentbordsfokus får programmet inte WPF-tangentbordshändelser och värdet för WPF-egenskapen IsKeyboardFocusWithin kommer att vara
false
.När fokus ligger inom HwndHost och ändras till en annan kontroll i HwndHostfår programmet inte WPF-händelserna GotFocus eller LostFocus.
Relaterade stylusegenskaper och händelser är analoga och rapporterar inte information medan pennan är över HwndHost.
Tabbing, Mnemonics och acceleratorer
Med gränssnitten IKeyboardInputSink och IKeyboardInputSite kan du skapa en sömlös tangentbordsupplevelse för mixade WPF- och Win32-program:
Tabbing mellan Win32- och WPF-komponenter
Mnemonics och acceleratorer som fungerar både när fokus ligger inom en Win32-komponent och när den är inom en WPF-komponent.
Klasserna HwndHost och HwndSource tillhandahåller båda implementeringar av IKeyboardInputSink, men de kanske inte hanterar alla indatameddelanden som du vill ha för mer avancerade scenarier. Åsidosätt lämpliga metoder för att få det tangentbordsbeteende du vill ha.
Gränssnitten ger endast stöd för vad som händer vid övergången mellan WPF- och Win32-regionerna. I Win32-regionen styrs tabbbeteendet helt av win32-implementerad logik för tabbning, om sådan finns.
Se även
.NET Desktop feedback