Interopérabilité WPF et Win32

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 du contenu WPF. Grâce à cette technique, vous pouvez utiliser un contrôle Win32 personnalisé existant dans le contexte d'un autre contenu WPF, puis 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 de contenu WPF dans Win32. Pour obtenir une illustration plus orientée code de l'hébergement de Win32 dans WPF, consultez Procédure pas à pas : hébergement d'un contrôle Win32 dans WPF.

Projets d'interopérabilité WPF

Les interfaces APIs WPF 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 APIs WPF à 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 Extensible Application Markup Language (XAML) 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 sous la forme d'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 faites-le référencer une DLL C++ 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.

RemarqueRemarque

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 de C++/CLI, consultez Language Features for Targeting the CLR et Hello, C++/CLI (page éventuellement en anglais).

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 Vue d'ensemble des régions de technologie.

Tous les éléments WPF affichés à l'écran sont stockés par un HWND. Lorsque vous créez un Window WPF, 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. Ces éléments créent leur propre fenêtre de niveau supérieur. Par conséquent, 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 Window WPF.

La transparence dans et entre les HWND constitue un concept connexe des HWND. Il est également abordé à la rubrique Vue d'ensemble des régions de technologie.

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 user interface (UI) en tant que fenêtre enfant. L'approche suivante combine Win32 et WPF dans une application unique.

  1. 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 ».

  2. 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).

  3. Affectez au modèle de thread un thread cloisonné (STA, Single-Threaded Apartment). WPF utilise ce modèle de thread.

  4. Gérez la notification WM_CREATE dans votre procédure de fenêtre.

  5. Dans le gestionnaire (ou une fonction appelée par le gestionnaire), effectuez les opérations suivantes :

    1. Créez un objet HwndSource en utilisant le HWND de fenêtre parente comme paramètre parent.

    2. Créez une instance de la classe de contenu WPF.

    3. Attribuez une référence à l'objet de contenu WPF à la propriété RootVisual de l'objet HwndSource.

    4. 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.

  6. 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.

  7. Recevez les notifications de l'objet de contenu WPF en attachant un gestionnaire à un ou plusieurs des événements d'objet de contenu WPF.

  8. 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.

RemarqueRemarque

Vous pouvez effectuer l'intégralité ou bien une partie de la définition de la classe de contenu WPF à 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 dans un 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 fichiersXAML 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 de contenu WPF dans 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 englobe la fenêtre dans un wrapper dans un élément WPF qui peut être ajouté à une arborescence d'éléments WPF. HwndHost prend également en charge les APIs qui vous permettent de réaliser certaines tâches telles que le traitement des messages de la fenêtre hébergée. La procédure de base est la suivante :

  1. 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".

  2. Effectuez une dérivation de HwndHost afin de créer un objet stockant le contenu Win32.

  3. Dans cette classe hôte, substituez la méthode BuildWindowCore HwndHost. Retournez le HWND de la fenêtre hébergée. Vous pouvez inclure dans un wrapper le ou les contrôles réels en tant que fenêtre enfant de la fenêtre retournée. L'inclusion des contrôles dans un wrapper 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.

  4. Substituez les méthodes HwndHost DestroyWindowCore 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.

  5. 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.

  6. 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 HwndHost WndProc.

    • 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.

  7. 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 dans WPF.

HWND dans WPF

Vous pouvez envisager HwndHost comme un contrôle spécial. (théoriquement, HwndHost est une classe dérivée de FrameworkElement, et non une classe dérivée de Control, mais elle peut être considérée comme un contrôle à des fins d'interopérabilité). HwndHost soustrait la nature Win32 sous-jacente du contenu hébergé de sorte que le reste de WPF considère le contenu hébergé comme un autre objet de contrôle, qui doit restituer et traiter l'entrée. HwndHostse comporte généralement comme tout autre WPFFrameworkElement, bien qu'il y ait des différences importantes en termes de sortie (dessin et graphiques) et d'entrée (souris et clavier) dues aux limitations de prise en charge des 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 possibles de Win32, 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é WPF IsMouseOver 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 WPF GotFocus 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.

Voir aussi

Référence

HwndHost

HwndSource

System.Windows.Interop

Concepts

Procédure pas à pas : hébergement d'un contrôle Win32 dans WPF

Procédure pas à pas : hébergement de contenu WPF dans Win32