Partager via


Procédure pas à pas : hébergement du contenu Windows Presentation Foundation dans une application Win32

Mise à jour : novembre 2007

Windows Presentation Foundation (WPF) fournit un environnement riche pour créer des applications. Toutefois, lorsque vous avez un investissement substantiel dans du code Win32, il peut s'avérer plus efficace d'ajouter la fonctionnalité WPF à votre application plutôt que de réécrire votre code d'origine. WPF fournit un mécanisme simple pour héberger le contenu WPF dans une fenêtre Win32.

Ce didacticiel décrit comment écrire un exemple d'application, Hébergement du contenu Windows Presentation Foundation dans une fenêtre Win32, exemple, qui héberge du contenu WPF dans une fenêtre Win32. Vous pouvez étendre cet exemple pour héberger toute fenêtre Win32. Du fait qu'elle implique un mélange de code managé et non managé, l'application est écrite en C++/CLI.

Cette rubrique comprend les sections suivantes.

  • Configuration requise
  • La procédure de base :
  • Implémentation de l'application hôte
  • Implémentation de la page WPF
  • Rubriques connexes

Configuration requise

Ce didacticiel suppose que avez des connaissances de base en matière de programmation WPF et Win32. Pour une présentation générale de la programmation WPF, consultez Mise en route (WPF). Pour une présentation de la programmation Win32, consultez les divers manuels qui traitent de ce sujet, notamment La programmation Windows de Charles Petzold.

Étant donné que l'exemple qui accompagne ce didacticiel est implémenté en C++/CLI, ce didacticiel suppose que vous savez utiliser C++ pour programmer l'API Win32 et que vous comprenez la programmation de code managé. Il est également souhaitable de connaître C++/CLI, mais cela n'est pas essentiel.

Remarque :

Ce didacticiel inclut des exemples de code de l'exemple associé. Toutefois, il n'inclut pas l'exemple de code complet pour une meilleure lisibilité. Pour obtenir l'exemple de code complet, consultez Hébergement du contenu Windows Presentation Foundation dans une fenêtre Win32, exemple.

La procédure de base :

Cette section présente la procédure de base à utiliser pour héberger le contenu WPF dans une fenêtre Win32. Les sections restantes décrivent chaque étape en détail.

L'élément principal de l'hébergement d'un contenu WPF dans une fenêtre Win32 est la classe HwndSource. Cette classe encapsule le contenu WPF dans une fenêtre Win32, permettant ainsi son incorporation dans votre interface utilisateur (UI) sous la forme d'une fenêtre enfant. L'approche suivante combine Win32 et WPF dans une application unique.

  1. Implémentez votre contenu WPF comme une classe managée.

  2. Implémentez une application Win32 avec C++/CLI. Si vous commencez avec une application existante et du code C++ non managé, vous pouvez généralement lui permettre d'appeler le code managé en modifiant les paramètres de votre projet de manière à inclure l'indicateur de compilateur /clr.

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

  4. Gérez la notification WM_CREATE dans votre procédure de fenêtre et procédez comme suit :

    1. Créez un objet HwndSource en utilisant la 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é HwndSource de RootVisual.

    4. Obtenez le HWND du contenu. La propriété HwndSource de l'objet Handle 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.

  5. Implémentez une classe managée qui contient un champ statique comportant une référence à votre contenu WPF. Cette classe vous permet d'obtenir une référence au contenu WPF à partir de votre code Win32.

  6. Assignez le contenu WPF au champ statique.

  7. Recevez des notifications du contenu WPF en attachant un gestionnaire à un ou plusieurs événements WPF.

  8. Communiquez avec le contenu WPF à l'aide de la référence que vous avez stockée dans le champ statique pour définir des propriétés, etc.

Remarque :

Vous pouvez également utiliser XAML (Extensible Application Markup Language) pour implémenter votre contenu WPF. Toutefois, vous devrez le compiler séparément comme une bibliothèque de liens dynamiques (DLL) et référencer cette DLL à partir de votre application Win32. Le reste de la procédure est similaire à celle présentée ci-dessus.

Implémentation de l'application hôte

Cette section décrit comment héberger le contenu WPF dans une application Win32 de base. Le contenu proprement dit est implémenté dans C++/CLI comme une classe managée. Pour la plupart, il s'agit de programmation WPF simple. Les principaux aspects de l'implémentation de contenu sont décrits à la section Implémentation du contenu WPF.

  • Application de base

  • Hébergement du contenu WPF

  • Stockage d'une référence au contenu WPF

  • Communication avec le contenu WPF

Application de base

Le point de départ pour l'application hôte est la création d'un modèle Microsoft Visual Studio 2005.

  1. Ouvrez Visual Studio 2005; puis sélectionnez Nouveau projet dans le menu Fichier.

  2. Sélectionnez Win32 dans la liste des types de projets Visual C++. Si votre langue par défaut n'est pas C++, vous trouverez ces types de projets sous Autres langages.

  3. Sélectionnez un modèle Projet Win32, attribuez un nom au projet, puis cliquez sur OK pour lancer l' Assistant Application Win32.

  4. Acceptez les paramètres par défaut de l'Assistant, puis cliquez sur Terminer pour démarrer le projet.

Le modèle crée une application Win32 de base, notamment :

  • Le point d'entrée de l'application.

  • Une fenêtre, avec une procédure de fenêtre associée (WndProc).

  • Un menu avec les intitulés Fichier et ? (Aide). Le menu Fichier comporte un élément Quitter qui ferme l'application. Le menu ? (Aide) comprend un élément À propos de qui lance une boîte de dialogue simple.

Avant de commencer à écrire le code pour héberger le contenu WPF, vous devez apporter deux modifications au modèle de base.

Le premier consiste à compiler le projet comme code managé. Par défaut, le projet est compile comme code non managé. Toutefois, étant donné que WPF est implémenté en code managé, le projet doit être compilé en conséquence.

  1. Cliquez avec le bouton droit sur le nom du projet dans l'Explorateur de solutions, puis sélectionnez Propriétés dans le menu contextuel afin d'ouvrir la boîte de dialogue Pages de propriétés.

  2. Sélectionnez Propriétés de configuration dans le volet gauche de l'arborescence.

  3. Sélectionnez la prise en charge du Common Language Runtime dans la liste Paramètres par défaut du projet dans le volet droit.

  4. Sélectionnez Prise en charge du Common Language Runtime (/clr) dans la zone de liste déroulante.

Remarque :

Cet indicateur de compilateur vous permet d'utiliser le code managé dans votre application, mais la compilation de votre code non managé se fera toujours comme avant.

WPF utilise un modèle de thread cloisonné (STA, Single-Threaded Apartment). Pour utiliser correctement le code de contenu WPF, vous devez affecter au modèle de thread de l'application la valeur STA en appliquant un attribut au point d'entrée.

Hébergement du contenu WPF

Le contenu WPF est une simple application d'entrée d'adresse. Il consiste en plusieurs contrôles TextBox destinés à prendre le nom d'utilisateur, l'adresse, etc. Il existe également deux contrôles Button, OK et Annuler. Lorsque l'utilisateur clique sur OK, le gestionnaire d'événements Click du bouton recueille les données des contrôles TextBox, les assigne aux propriétés correspondantes, puis déclenche l'événement personnalisé OnButtonClicked. Lorsque l'utilisateur clique sur Annuler, le gestionnaire déclenche simplement OnButtonClicked. L'objet d'argument d'événement pour OnButtonClicked contient un champ Boolean qui indique le bouton sur lequel l'utilisateur a cliqué.

Le code d'hébergement du contenu WPF est implémenté dans un gestionnaire pour la notification WM_CREATE dans la fenêtre hôte.

La méthode GetHwnd prend les informations relatives à la taille et à la position plus le handle de la fenêtre parente et retourne le handle de fenêtre du contenu WPF hébergé.

Remarque :

Vous ne pouvez pas utiliser une directive #using pour l'espace de noms System::Windows::Interop. L'utilisation d'une telle directive entraîne une collision de noms entre la structure MSG dans cet espace de noms et la structure MSG déclarée dans winuser.h. Au lieu de cela, vous devez utiliser des noms complets pour accéder au contenu de cet espace de noms.

Vous ne pouvez pas héberger directement le contenu WPF dans votre fenêtre d'application. Vous devez plutôt commencer par créer un objet HwndSource qui encapsulera le contenu WPF. Cet objet est essentiellement une fenêtre conçue pour héberger un contenu WPF. Vous hébergez l'objet HwndSource dans la fenêtre parente en le créant comme un enfant d'une fenêtre Win32 qui fait partie de votre application. Les paramètres du constructeur HwndSource contiennent quasiment les mêmes informations que vous transmettriez à CreateWindow à la création d'une fenêtre enfant Win32.

Vous créez ensuite une instance de l'objet de contenu WPF. Dans ce cas, le contenu WPF est implémenté comme une classe distincte, WPFPage, à l'aide de C++/CLI. Vous pouvez également implémenter le contenu WPF avec XAML. Toutefois, vous devez pour cela installer un projet distinct et générer le contenu WPF comme une DLL. Vous pouvez ajouter à votre projet une référence à cette DLL et utiliser cette référence pour créer une instance du contenu WPF.

Vous affichez le contenu WPF dans votre fenêtre enfant en assignant à la propriété RootVisual du HwndSource une référence au contenu WPF.

La ligne de code suivante attache le gestionnaire d'événements WPFButtonClicked à l'événement OnButtonClicked du contenu WPF. Ce gestionnaire est appelé lorsque l'utilisateur clique sur le bouton OK ou Annuler. Consultez communicating_with_the_WPF content pour plus d'informations sur ce gestionnaire d'événements.

La dernière ligne de code illustrée retourne le handle de fenêtre (HWND) associé à l'objet HwndSource. Vous pouvez utiliser ce handle à partir de votre code Win32 pour envoyer des messages à la fenêtre hébergée, bien que l'exemple ne le fasse pas. L'objet HwndSource déclenche un événement à chaque fois qu'il reçoit un message. Pour traiter les messages, appelez la méthode AddHook pour attacher un gestionnaire de messages, puis traitez les messages dans ce gestionnaire.

Stockage d'une référence au contenu WPF

Pour de nombreuses applications, vous souhaiterez communiquer ultérieurement avec le contenu WPF. Par exemple, vous pouvez souhaiter modifier les propriétés du contenu WPF ou éventuellement faire en sorte que l'objet HwndSource héberge un contenu WPF différent. Pour ce faire, vous avez besoin d'une référence à l'objet HwndSource ou au contenu WPF. L'objet HwndSource et son contenu WPF associé restent en mémoire jusqu'à la destruction du handle de fenêtre. Toutefois, la variable que vous assignez à l'objet HwndSource sera hors de portée dès que vous retournerez de la procédure de fenêtre. La méthode courante employée pour gérer ce problème avec les applications Win32 consiste à utiliser une variable statique ou globale. Malheureusement, il n'est pas possible d'assigner un objet managé à ces types de variables. Vous pouvez assigner le handle de fenêtre associé à l'objet HwndSource à une variable globale ou statique, mais cela ne permet pas d'accéder à l'objet proprement dit.

La solution la plus simple à ce problème consiste à implémenter une classe managée qui contient un jeu de champs statiques pour stocker les références à tous les objets managés auxquels vous devez accéder. L'exemple utilise la classe WPFPageHost pour stocker une référence au contenu WPF, plus les valeurs initiales de plusieurs de ses propriétés qui peuvent être modifiées ultérieurement par l'utilisateur. Cette option est définie dans l'en-tête.

La dernière partie de la fonction GetHwnd assigne des valeurs à ces champs en vue d'une utilisation ultérieure pendant que myPage est encore dans la portée.

Communication avec le contenu WPF

Il existe deux types de communication avec le contenu WPF. L'application reçoit des informations du contenu WPF lorsque l'utilisateur clique sur le bouton OK ou Annuler. L'application a également une interface utilisateur qui permet à l'utilisateur de modifier différentes propriétés du contenu WPF, telles que la couleur d'arrière-plan ou la taille de police par défaut.

Comme indiqué précédemment, lorsque l'utilisateur clique sur l'un des boutons, le contenu WPF déclenche un événement OnButtonClicked. L'application attache un gestionnaire à cet événement pour recevoir ces notifications. Si l'utilisateur a cliqué sur le bouton d'OK, le gestionnaire obtient les informations utilisateur à partir du contenu WPF et les affiche dans un jeu de contrôles statiques.

Le gestionnaire reçoit un objet d'argument d'événement personnalisé du contenu WPF, MyPageEventArgs. La propriété IsOK de l'objet a la valeur true si l'utilisateur a cliqué sur le bouton OK et elle a la valeur false si l'utilisateur a cliqué sur le bouton Annuler.

Si l'utilisateur a cliqué sur le bouton OK, le gestionnaire obtient une référence au contenu WPF à partir de la classe du conteneur. Il recueille ensuite les informations utilisateur qui sont détenues par les propriétés de contenu WPF associées et il utilise les contrôles statiques pour afficher ces informations dans la fenêtre parente. Étant donné que les données de contenu WPF se présentent au format d'une chaîne managée, elles doivent être marshalées en vue de leur utilisation par un contrôle Win32. Si l'utilisateur a cliqué sur le bouton Annuler, le gestionnaire efface les données des contrôles statiques.

L'interface utilisateur de l'application fournit un jeu de cases d'option qui permettent à l'utilisateur de modifier la couleur d'arrière-plan du contenu WPF ainsi que plusieurs propriétés liées aux polices. L'exemple suivant est un extrait de la procédure de fenêtre de l'application (WndProc) et de son traitement de messages qui définit différentes propriétés sur différents messages, notamment la couleur d'arrière-plan. Les autres procédures sont similaires et ne sont pas illustrées. Consultez l'exemple complet pour plus de détails et pour connaître le contexte.

Pour définir la couleur d'arrière-plan, obtenez une référence au contenu WPF (hostedPage) à partir de WPFPageHost et affectez à la propriété de couleur d'arrière-plan la valeur appropriée. L'exemple utilise trois options de couleur : la couleur d'origine, du vert clair ou du saumon clair. La couleur d'arrière-plan d'origine est stockée comme un champ statique dans la classe WPFPageHost. Pour définir les deux autres couleurs, vous devez créer un objet SolidColorBrush et transmettre une valeur de couleur statique au constructeur à partir de l'objet Colors.

Implémentation de la page WPF

Vous pouvez héberger et utiliser le contenu WPF sans avoir aucune connaissance de l'implémentation réelle. Si le contenu WPF avait été conditionné dans une DLL distincte, il aurait pu être construit en tout langage Common Language Runtime (CLR). Une brève procédure pas à pas de l'implémentation C++/CLI utilisée dans l'exemple est décrite ci-dessous. Cette section comprend les sous-sections suivantes.

  • Disposition

  • Retourner les données à la fenêtre hôte

  • Définition des propriétés WPF

Disposition

Les éléments de l'interface utilisateur dans le contenu WPF consistent en cinq contrôles TextBox, avec les contrôles Label associés : Nom, Adresse, Ville, État et Code postal. Il existe également deux contrôles Button, OK et Annuler

Le contenu WPF est implémenté dans la classe WPFPage. La disposition est gérée avec un élément de disposition Grid. La classe hérite de Grid, qui en fait effectivement l'élément racine du contenu WPF.

Le constructeur de contenu WPF prend la largeur et la hauteur requises, et dimensionne le Grid en conséquence. Il définit ensuite la disposition de base en créant un jeu d'objets ColumnDefinition et RowDefinition et en les ajoutant à la base d'objet GridColumnDefinitions et aux collections RowDefinitions, respectivement. Une grille contenant cinq lignes et sept colonnes est ainsi définie et ses dimensions sont déterminées par le contenu des cellules.

Le constructeur ajoute ensuite les éléments de l'interface utilisateur au Grid. Le premier élément est le texte du titre, qui est un contrôle Label centré dans la première ligne de la grille.

La ligne suivante contient le contrôle Name Label et son contrôle TextBox associé. Étant donné que le même code est utilisé pour chaque paire d'étiquette/zone de texte, il est placé dans une paire de méthodes privées et est utilisé pour les cinq paires d'étiquette/zone de texte. Les méthodes créent le contrôle approprié et appellent les méthodes SetColumn et SetRow statiques de la classe Grid pour placer les contrôles dans la cellule appropriée. Une fois le contrôle créé, l'exemple appelle la méthode Add sur la propriété Children du Grid pour ajouter le contrôle dans la grille. Le code permettant d'ajouter les paires d'étiquette/zone de texte restantes est similaire. Consultez l'exemple de code pour plus d'informations.

Les deux méthodes sont implémentées comme suit :

Enfin, l'exemple ajoute les boutons OK et Annuler et attache un gestionnaire d'événements à leurs événements Click.

Retourner les données à la fenêtre hôte

Lorsque l'utilisateur clique sur l'un des boutons, son événement Click est déclenché. La fenêtre hôte peut attacher simplement des gestionnaires à ces événements et obtenir directement les données des contrôles TextBox. L'exemple met en œuvre une approche un peu moins directe. Il gère le Click dans le contenu WPF, puis déclenche un événement OnButtonClickedpersonnalisé, pour notifier le contenu WPF. Cela permet au contenu WPF de valider quelques paramètres avant de notifier l'hôte. Le gestionnaire obtient le texte des contrôles TextBox et l'assigne aux propriétés publiques, à partir desquelles l'hôte peut extraire les informations.

La déclaration event, dans WPFPage.h :

Le gestionnaire d'événements Click, dans WPFPage.cpp :

Définition des propriétés WPF

L'hôte Win32 permet à l'utilisateur de modifier plusieurs propriétés de contenu WPF. Du côté Win32, il s'agit simplement de modifier des propriétés. L'implémentation dans la classe de contenu WPF est plus complexe, car il n'existe aucune propriété globale unique qui contrôle les polices pour tous les contrôles. Au lieu de cela, la propriété appropriée de chaque contrôle est modifiée dans les accesseurs set des propriétés. L'exemple suivant illustre le code de la propriété DefaultFontFamily. La définition de la propriété appelle une méthode privée qui, ensuite, définit les propriétés FontFamily de plusieurs contrôles.

À partir de WPFPage.h :

À partir de WPFPage.cpp :

Voir aussi

Concepts

Vue d'ensemble de l'interopérabilité WPF et Win32

Référence

HwndSource