Vue d'ensemble de l'interopérabilité WPF et Win32
Mise à jour : novembre 2007
Cette rubrique fournit une vue d'ensemble de l'interopérabilité du code WPF et Win32. Windows Presentation Foundation (WPF) fournit un environnement riche pour créer des applications. Toutefois, si vous avez beaucoup investi dans du code Win32, il peut s'avérer plus intéressant de réutiliser une partie de ce code.
Cette rubrique comprend les sections suivantes.
- Notions de base de l'interopérabilité WPF et Win32
- Projets d'interopérabilité WPF
- Utilisation des HWND par WPF
- Hébergement de contenu WPF dans une fenêtre Microsoft Win32
- Hébergement d'une fenêtre Microsoft Win32 dans WPF
- Tabulation, mnémoniques et accélérateurs
- Rubriques connexes
Notions de base de l'interopérabilité WPF et Win32
Il existe deux techniques de base liées à l'interopérabilité entre du code WPF et Win32.
Hébergement de contenu WPF dans une fenêtre Win32. Grâce cette technique, vous pouvez bénéficier des capacités graphiques avancées de WPF dans l'infrastructure d'une application et d'une fenêtre Win32 standard.
Hébergement d'une fenêtre Win32 dans WPF. Grâce à cette technique, vous pouvez utiliser un contrôle Win32 personnalisé existant dans le contexte d'un autre contenu WPF et passer des données au-delà des limites.
Les concepts inhérents à chacune de ces techniques sont présentés dans cette rubrique. Pour obtenir une illustration plus orientée code de l'hébergement de WPF dans Win32, consultez Procédure pas à pas : hébergement d'un contrôle Win32 simple dans une application Windows Presentation Foundation. Pour obtenir une illustration plus orientée code de l'hébergement de Win32 dans WPF, consultez Procédure pas à pas : hébergement du contenu Windows Presentation Foundation dans une application Win32.
Projets d'interopérabilité WPF
Les interfaces APIWPF sont constituées de code managé. Toutefois, la plupart des programmes Win32 existants sont écrits en C++ non managé. Il est impossible d'appeler une interface APIWPF à partir d'un vrai programme non managé. Cependant, si vous utilisez l'option /clr avec le compilateur Microsoft Visual C++, vous pouvez créer un programme managé/non managé mixte dans lequel vous pouvez mélanger des appels API managés et non managés de façon transparente.
L'un des problèmes au niveau du projet est l'impossibilité de compiler des fichiers XAML (Extensible Application Markup Language) dans un projet C++. Il existe toutefois plusieurs techniques de division du projet qui permettent de contourner ce problème.
Créez une DLL C# qui contient l'ensemble de vos pages XAML en tant qu'assembly compilé, puis intégrez cette DLL en tant que référence dans le fichier exécutable C++.
Créez un exécutable C# pour le contenu WPF et référencez-le à une DLLC++ qui intègre le contenu Win32.
Utilisez Load pour charger le langage XAML au moment de l'exécution, au lieu de compiler le langage XAML.
N'utilisez pas le langage XAML et écrivez tous les éléments WPF dans du code, en élaborant l'arborescence d'éléments à partir de Application.
Utilisez la technique qui vous convient le mieux.
Remarque : |
---|
Si vous n'avez jamais utilisé C++/CLI, vous pourrez constater quelques "nouveaux" mots clés tels que gcnew et nullptr dans les exemples de code d'interopérabilité. Ces mots clés remplacent l'ancienne syntaxe à double trait de soulignement (__gc) et fournissent une syntaxe plus naturelle pour le code managé en C++. Pour en savoir plus sur les fonctionnalités managées C++/CLI, consultez Language Features for Targeting the CLR et Hello, C++/CLI. |
Utilisation des HWND par WPF
Pour tirer le meilleur parti de l'interopérabilité HWND dans WPF, vous devez comprendre comment WPF utilise les HWND. Pour un HWND, vous ne pouvez pas mélanger le rendu WPF avec le rendu DirectX ou GDI / GDI+, ce qui entraîne plusieurs implications. D'une part, pour pouvoir mélanger ces modèles de rendu, vous devez créer une solution d'interopérabilité et utiliser les segments d'interopérabilité désignés pour chaque modèle de rendu à utiliser. D'autre part, le comportement de rendu crée une restriction d'« espace de rendu » ('airspace' en anglais) relative aux objectifs de la solution d'interopérabilité. Le concept d'« espace de rendu » est expliqué en détail dans la rubrique Interopérabilité de WPF : vue d'ensemble des zones de fenêtre et de l'espace de rendu.
Tous les éléments WPF affichés sur l'écran sont stockés par un HWND. Lorsque vous créez un WindowWPF, WPF crée un HWND de niveau supérieur et utilise un HwndSource pour placer Window et son contenu WPF dans le HWND. Le reste du contenu WPF de l'application partage ce HWND singulier. Les menus, les zones de liste déroulante et les autres menus contextuels constituent une exception à ce principe. En effet, ces éléments créent leur propre fenêtre de niveau supérieur. Dès lors, un menu WPF peut potentiellement dépasser le bord du HWND de fenêtre qui le contient. Si vous utilisez HwndHost pour placer un HWND dans WPF, WPF indique à Win32 comment positionner le nouveau HWND enfant par rapport au HWND WindowWPF.
La transparence dans et entre les HWND constitue un concept connexe des HWND. Il est également abordé à la rubrique Interopérabilité de WPF : vue d'ensemble des zones de fenêtre et de l'espace de rendu.
Hébergement de contenu WPF dans une fenêtre Microsoft Win32
L'élément principal de l'hébergement d'un objet WPF dans une fenêtre Win32 est la classe HwndSource. Cette classe encapsule le contenu WPF dans une fenêtre Win32, afin que le contenu WPF puisse être incorporé dans votre interface utilisateur (UI) en tant que fenêtre enfant. L'approche suivante combine Win32 et WPF dans une application unique.
Implémentez le contenu WPF (l'élément racine de contenu) en tant que classe managée. En règle générale, la classe hérite d'une classe qui peut contenir plusieurs éléments enfants et/ou être utilisée en tant qu'élément racine, comme DockPanel ou Page. Dans les étapes suivantes, cette classe est appelée "classe de contenu WPF" et les instances de la classe sont appelées "objets de contenu WPF".
Implémentez une application Win32 avec C++/CLI. Si vous démarrez avec une application C++ non managée existante, vous pouvez généralement lui permettre d'appeler le code managé en modifiant les paramètres du projet afin d'inclure l'indicateur de compilateur /clr (cette rubrique ne décrit pas la portée complète des éléments nécessaires à la prise en charge de la compilation /clr).
Définissez le modèle de thread en tant que thread unique cloisonné (STA). WPF utilise ce modèle de thread.
Gérez la notification WM_CREATE dans votre procédure de fenêtre.
Dans le gestionnaire (ou une fonction appelée par le gestionnaire), effectuez les opérations suivantes :
Créez un objet HwndSource en utilisant le HWND de fenêtre parente comme paramètre parent.
Créez une instance de la classe de contenu WPF.
Attribuez une référence à l'objet de contenu WPF à la propriété RootVisual de l'objet HwndSource.
La propriété Handle de l'objet HwndSource contient le handle de fenêtre (HWND). Pour obtenir un HWND que vous pouvez utiliser dans la partie non managée de l'application, effectuez un cast de Handle.ToPointer() en un HWND.
Implémentez une classe managée qui contient un champ statique comportant une référence à l'objet de contenu WPF. Cette classe permet d'obtenir une référence à l'objet de contenu WPF à partir du code Win32 et, surtout, empêche HwndSource d'être récupéré par inadvertance par le garbage collector.
Recevez des notifications de l'objet de contenu WPF en attachant un gestionnaire à un ou plusieurs des événements d'objet de contenu WPF.
Communiquez avec l'objet de contenu WPF à l'aide de la référence stockée dans le champ statique pour définir des propriétés, appeler des méthodes, etc.
Remarque : |
---|
Vous pouvez effectuer une partie ou l'ensemble de la définition de la classe de contenu WPF pour la première étape en langage XAML. Pour cela, utilisez la classe partielle par défaut de la classe de contenu, si vous produisez un assembly distinct avant de le référencer. En général, vous intégrez un objet Application dans le cadre de la compilation du langage XAML en assembly. Toutefois, vous n'utilisez pas ce Application dans le cadre de l'interopérabilité ; vous utilisez simplement une ou plusieurs des classes racines pour les fichiers XAML référencés par l'application et référencez leurs classes partielles. Le reste de la procédure est semblable à celle présentée ci-dessus. Chacune de ces étapes est illustrée à l'aide de code à la rubrique Procédure pas à pas : hébergement du contenu Windows Presentation Foundation dans une application Win32. |
Hébergement d'une fenêtre Microsoft Win32 dans WPF
L'élément principal de l'hébergement d'une fenêtre Win32 dans un autre contenu WPF est la classe HwndHost. Cette classe encapsule la fenêtre dans un élément WPF qui peut être ajouté à une arborescence d'éléments WPF. HwndHost prend également en charge les interfaces API, ce qui vous permet d'effectuer des tâches telles que le traitement de messages pour la fenêtre hébergée. La procédure de base est la suivante :
Créez une arborescence d'éléments pour une application WPF (à l'aide de code ou de balises). Recherchez un point approprié et autorisé dans l'arborescence d'éléments où l'implémentation HwndHost peut être ajoutée en tant qu'élément enfant. Dans les étapes suivantes, cet élément est appelé "élément de réservation".
Effectuez une dérivation de HwndHost afin de créer un objet stockant le contenu Win32.
Dans cette classe hôte, substituez la méthode BuildWindowCoreHwndHost. Retournez le HWND de la fenêtre hébergée. Vous pouvez encapsuler le ou les contrôles réels dans une fenêtre enfant de la fenêtre retournée. L'encapsulation des contrôles dans une fenêtre hôte permet au contenu WPF de recevoir facilement les notifications issues des contrôles. Cette technique permet de corriger certains problèmes Win32 liés à la gestion des messages au niveau de la limite de contrôles hébergés.
Substituez les méthodes HwndHostDestroyWindowCore et WndProc. L'objectif est de traiter le nettoyage et de supprimer les références au contenu hébergé, plus particulièrement si vous avez créé des références à des objets non managés.
Dans votre fichier code-behind, créez une instance de la classe d'hébergement de contrôles et définissez-la comme enfant de l'élément de réservation. En général, vous utilisez un gestionnaire d'événements (Loaded, par exemple) ou le constructeur de classe partielle. Vous pouvez toutefois ajouter le contenu d'interopérabilité avec un comportement au moment de l'exécution.
Traitez les messages de fenêtre sélectionnés, telles les notifications de contrôle. Les deux approches possibles fournissent un accès identique au flux de messages. Vous devez dès lors guider votre choix en fonction de la commodité de programmation.
Implémentez le traitement pour tous les messages (pas seulement les messages d'arrêt) dans la substitution de la méthode HwndHostWndProc.
Confiez le traitement des messages à l'élément WPF d'hébergement en gérant l'événement MessageHook. Cet événement est déclenché pour chaque message envoyé à la procédure de fenêtre principale de la fenêtre hébergée.
Vous ne pouvez pas traiter les messages provenant de fenêtres hors processus à l'aide de WndProc.
Communiquez avec la fenêtre hébergée à l'aide d'un appel de code non managé pour appeler la fonction SendMessage non managée.
L'exécution de ces étapes crée une application qui fonctionne avec les entrées de la souris. Vous pouvez ajouter la prise en charge de la tabulation pour la fenêtre hébergée en implémentant l'interface IKeyboardInputSink.
Chacune de ces étapes est illustrée à l'aide de code à la rubrique Procédure pas à pas : hébergement d'un contrôle Win32 simple dans une application Windows Presentation Foundation.
HWND dans WPF
Vous pouvez envisager HwndHost comme un contrôle spécial. (Du point de vue technique, HwndHost est une classe dérivée FrameworkElement, pas une classe dérivée Control. Il peut toutefois être considéré comme un contrôle à des fins d'interopérabilité.) HwndHost représente la nature Win32 sous-jacente du contenu hébergé. Ainsi, le reste de WPF traite le contenu hébergé comme s'il s'agissait d'un autre objet de type contrôle, qui doit restituer et traiter l'entrée. HwndHost se comporte généralement comme n'importe quel autre FrameworkElementWPF. Il existe toutefois des différences importantes liées à la sortie (dessins et graphiques) et à l'entrée (souris et clavier) en fonction des restrictions des éléments pris en charge par les HWND sous-jacents.
Principales différences dans le comportement de sortie
FrameworkElement, qui correspond à la classe de base HwndHost, comporte de nombreuses propriétés qui impliquent la modification de l'interface utilisateur. Il s'agit notamment de propriétés telles que FrameworkElement.FlowDirection, qui modifie la disposition des éléments dans cet élément en tant que parent. Toutefois, la plupart de ces propriétés ne sont pas mappées aux équivalents Win32 possibles, même si ces équivalents existent. Un nombre trop important de ces propriétés et de leur signification repose essentiellement sur la technologie de rendu, ce qui rend les mappages peu pratiques. Par conséquent, la définition de propriétés telles que FlowDirection dans HwndHost n'a aucun effet.
Une transformation n'a aucun impact sur HwndHost, par exemple pour le faire pivoter, le mettre à l'échelle ou l'incliner.
HwndHost ne prend pas en charge la propriété Opacity (fusion alpha). Si le contenu de HwndHost effectue des opérations System.Drawing qui incluent des informations alpha, il ne s'agit pas d'une violation en soi. Toutefois, dans son ensemble, HwndHost ne prend en charge qu'une opacité égale à 1.0 (100 %).
HwndHost apparaît sur les autres éléments WPF dans la même fenêtre de niveau supérieur. Toutefois, un menu généré ToolTip ou ContextMenu étant une fenêtre de niveau supérieur distincte, il se comporte correctement avec HwndHost.
HwndHost ne respecte pas la zone de découpage du UIElement parent. Il s'agit d'un problème potentiel si vous tentez de placer une classe HwndHost dans une zone de défilement ou Canvas.
Principales différences dans le comportement d'entrée
En général, lorsque la portée des périphériques d'entrée est définie dans la zone Win32 hébergée par HwndHost, les événements d'entrée sont directement placés dans Win32.
Lorsque la souris passe sur HwndHost, l'application ne reçoit pas d'événement de souris WPF et la propriété WPFIsMouseOver a la valeur false.
Lorsque HwndHost a le focus clavier, l'application ne reçoit pas d'événement de clavier WPF et la propriété WPFIsKeyboardFocusWithin a la valeur false.
Lorsque le focus est dans HwndHost et passe à un autre contrôle dans HwndHost, l'application ne reçoit pas les événements WPFGotFocus ou LostFocus.
Les événements et propriétés de stylet connexes sont semblables et ne signalent pas d'informations lorsque le stylet est sur HwndHost.
Tabulation, mnémoniques et accélérateurs
Grâce aux interfaces IKeyboardInputSink et IKeyboardInputSite, vous pouvez créer une expérience de clavier transparente pour les applications mixtes WPF et Win32 :
Tabulation entre des composants Win32 et WPF.
Mnémoniques et accélérateurs fonctionnant lorsque le focus est sur un composant Win32 et sur un composant WPF.
Les classes HwndHost et HwndSource fournissent des implémentations de IKeyboardInputSink, mais elles ne peuvent pas gérer tous les messages d'entrée nécessaires dans le cadre de scénarios plus évolués. Substituez les méthodes appropriées pour obtenir le comportement du clavier souhaité.
Les interfaces assurent uniquement la prise en charge des événements qui ont lieu lors de la transition entre les zones WPF et Win32. Dans la zone Win32, le comportement de tabulation est entièrement déterminé par la logique Win32 implémentée pour la tabulation, le cas échéant.