Intégrer des contrôles utilisateurs Windows Forms avec MFC

   Auteur : Bruno Boucard (boucard.bruno@free.fr)

Introduction
Application exemple
  Application Scribble
  It Just Works
  Intégration Windows Forms
Contrôle Windows Forms hébergé dans une boîte de dialogue MFC
  Motivation
  Développer un Contrôle Windows Forms
  Développer l’intégration MFC
  En résumé
Contrôle Windows Forms comme une boîte de dialogue MFC
  Motivation
  Développer un Contrôle Windows Forms
  Développer l’intégration MFC
  En résumé
Contrôle Windows Forms comme une vue MFC
  Motivation
  Développer un Contrôle Windows Forms
  Développer l’intégration MFC
  En résumé
MFC Interfaces Library
  Motivation
  Gestion des messages de type commande
    Introduction
    Exemple d’utilisation
    Zoom sur l’implémentation de l’intégration .NET
  Gestion de l’interface IView
    Introduction
    Exemple d’utilisation
   En résumé
Conclusion
Remerciements
Ressources

Introduction

vec l'avènement de la plateforme .NET, toute l’industrie informatique a été marqué par une avalanche de nouvelles technologies qui du même coup a relégué les outils de développement comme les Microsoft Foundation Classes (MFC) au rang des outils de développement « has been ». En effet la productivité de la plateforme .NET et la simplicité des Windows Forms vis-à-vis du couple C++/MFC, font de Windows Forms un choix plus attractif pour réaliser des écrans. Cependant de nombreuses entreprises (dont Microsoft) possèdent un lourd existant sur les vénérables classes MFC (par exemple MS Office), et le chant des sirènes Windows Forms est parfois difficile à entendre pour les développeurs d’applications MFC désireux d’offrir une cohérence visuelle pour leurs utilisateurs.

Pour les utilisateurs de Visual Studio.NET 2002-2003, la technologie Managed C++ peut être envisagée comme un début d’intégration graphique, car rien n’empêche de lancer une fenêtre Windows Forms au sein d’une application MFC (à condition d’ajouter le support du CLR au projet). Cependant, l’ergonomie graphique et la qualité de l’intégration ne sont pas toujours au rendez-vous : imaginez un utilisateur face à une application MFC du type Multiple Document Interface (MDI) dont certaines fenêtres Windows Forms semblent surgir d’une autre application, car ne partageant pas l’ergonomie des fenêtres MFC, et déconnectées du protocole MDI.

Si vous envisagez de migrer votre existant MFC sous Visual Studio 2005, vous bénéficierez d’un ensemble de nouvelles classes MFC dédiées à l’intégration graphique de contrôles utilisateurs Windows Forms. Techniquement ces classes permettent de traiter un contrôle utilisateur Windows Forms comme :

  • un contrôle au sein d’une boîte de dialogue MFC,
  • une boîte de dialogue MFC,
  • une vue MFC.

Nous passerons en revue ces trois cas à travers des chapitres dédiés. Dans chaque chapitre nous débuterons par une brève description fonctionnelle motivée par l’illustration d’un cas typique d’intégration d’un contrôle Windows Forms dans une application MFC exemple. Puis nous développerons respectivement un contrôle Windows Forms, son intégration .NET ; puis nous dresserons un petit bilan des efforts réalisés vis-à-vis de l’intégration. Enfin nous dresserons une conclusion globale sur l’intégration .NET/MFC. Nous jugerons à la fois sur la difficulté de mise en œuvre et le degré d’intégration visuelle des contrôles Windows Forms à travers les différents cas illustrés dans cet article.

Remarque : les exemples sont réalisés par du développement .NET, de l’intégration .NET- MFC et du code purement MFC. Cet article étant dédié à l’intégration des Windows Forms dans une application MFC, nous illustrerons uniquement le code relatif à l’intégration .NET, c'est-à-dire le code .NET et son intégration dans le code MFC. L’ensemble du code C++ et .NET est disponible à travers le code source téléchargeable.

Application exemple

Application Scribble

Les différents cas d’intégration de contrôles utilisateurs Windows Forms, seront présentés à travers une application que tous les vétérans MFC connaissent : l’application Scribble. L’application Scribble servait de tutoriel Microsoft aux développeurs désirant s’initier aux classes MFC à travers des sujets classiques : les bases du dessin avec GDI, l’usage du pattern observateur avec le modèle Document/Vue, les bases d’un serveur OLE Inplace. Si vous ne disposez pas de cette application, vous pouvez la télécharger depuis les exemples MFC fournis dans l’aide MSDN de Visual Studio 2005 en recherchant « SCRIBBLE Sample: MFC MDI Drawing Application », en filtrant sur le critère « Visual C++ ».

 

Figure 1 : sélection de l’exemple Scribble

Une fois recompilée vous obtenez une application graphique de type MDI permettant de dessiner des strokes de points en pressant le bouton droit de la souris, le tout via un environnement multi vues illustrant le modèle Document/Vue via une vue fractionnable verticalement et horizontalement. Ajoutons que la taille du pinceau est disponible par défaut en deux tailles, deux et cinq.

Figure 2 : application Scribble en action

It Just Works

A ce stade vous avez une application MFC native qui ne peut absolument pas intégrer des contrôles utilisateur Windows Forms. Pour y remédier, il vous faudra modifier les options de compilation du projet Scribble. Dans la section Configuration Properties->Général, positionnez la valeur de Common Language Runtime support sur Common Language Runtime support  (/clr). Dans la configuration Release, dans les propriétés C/C++, de la section Code Generation, modifiez la valeur positionnée par défaut, Enable C++ Exceptions : Yes (/EHsc) car elle-ci n’est pas compatible avec l’option /clr. Vous pouvez choisir l’option Yes With SEH Exceptions (/EHa) compatible avec l’option /clr.

Figure 3 : configuration des propriétés C++ du projet Scribble vis-à-vis du support du CLR

Remarque : si votre application est actuellement sous Visual Studio 2003 vous devrez au préalable la convertir via l’assistant Visual Studio 2005 puis la recompiler et constater si votre code ne pose aucun problème. Enfin vous ne pourriez pas compiler votre application avec le support de la CLR, si celle-ci ne linke pas les librairies MFC en mode dynamique (_AFXDLL).

Cette option /clr permet de compiler du code C++ dans un mode mixe, où du code natif cohabite avec du code géré (Managed C++ ou C++ / CLI) ; en d’autres mots les classes C++ pourront manipuler des classes .NET sans difficulté. Après avoir recompilé l’application, vous obtenez un exécutable mixte, c'est-à-dire un binaire contenant du code natif et du code intermédiaire .NET (IL). Après compilation, l’exécution du nouveau binaire charge le moteur d’exécution .NET. La capacité du compilateur C++ avec l’option /clr, qui permet de transformer une application C++ en une « application .NET », existe depuis la toute première version du Framework .NET. Les équipes Microsoft appellent aussi ce tour de force technologique, It Just Works (IJW). Cependant, Visual Studio 2005 apporte en plus du langage C++ / CLI (successeur avantageux des extensions gérées du Managed C++), des optimisations significatives dans la manière d’inter-opérer entre le C++ natif et des classes .NET. Nous ne détaillerons pas ici, comment fonctionne IJW, mais retenons que c’est la technologie Platform Invoke (P/Invoke) qui est engendrée par le compilateur. Pour vous plus d’information sur les améliorations apporté par le compilateur C++ de Visual Studio 2005, je vous invite à lire l’excellent article de Stephen Toub : Write Faster Code with the Modern Language Features of Visual C++ 2005 (http://msdn.microsoft.com/msdnmag/issues/04/05/VisualC2005/). Sinon vous pouvez lancer un Process Explorer (www.sysinternals.com) et dans le panneau DLL, observer les librairies dynamiques (DLLs) dédiées à la CLR, chargées par la nouvelle version de l’application Scribble.

Figure 4 : visualisation des librairies dynamiques du CLR chargées par l’application Scribble

Remarque : les options /clr et /clr:oldSyntax (compatible Managed C++ VS 2002 – VS 2003) engendrent des assemblages complètements unsafe et donc peu compatibles avec un déploiement « No Touch » souvent relaté avec ClickOnce. Cependant les applications MFC (clients lourds) sont généralement déployées via des packages type MSI qui réclament des droits importants et donc par conséquent les contraintes de sécurité CAS sont plus tolérables.

Les /clr:safe et /clr:pure ne sont pas utilisables car le code C++ natif ne peut pas être réellement traduit en code intermédiaire .NET.

Intégration Windows Forms

Vous avez maintenant une application MFC prête à intégrer du code .NET, et donc pourquoi pas, des écrans de contrôleWindows Forms. Techniquement les contrôles Windows Forms peuvent être produits par tous les langages supportés dans Visual Studio 2005. Vous pouvez au sein de la solution Scribble, ajouter un projet (ici Visual C#) de type Windows, et choisir le patron Windows Control Library. Dans les exemples j’ai nommé le projet ScribbleUserControl.

Figure 5 : ajout d’un projet C# Windows Forms de type User Control

Une fois avoir accepté des valeurs proposées, vous obtenez un contrôle utilisateur complètement vierge, que vous pouvez compiler immédiatement. A ce stade vous pouvez ajouter dans le projet Scribble, l’inclusion du fichier « afxWinForms.h » dans le fichier « stdafx.h ».

Figure 6 : ajout de l’inclusion de fichier afxWinForms.h dans le fichier stdafx.h de l’application Scribble

Ce fichier contient toutes les définitions des classes MFC permettant d’héberger des contrôles utilisateur Windows Forms. Les classes C++ / CLI par nature ne peuvent être héritées d’une classe C++ natives, en revanche une classe C++ / CLI peut être agrégée au sein d’une classe C++ native. Donc une fois encapsulée dans une classe C++ native comme proposé dans le fichier afxWinForms.h, les intentions de réutilisation sont moins restreintes.

Figure 7 : principe des classes MFC d’intégration des Windows Forms

En quelques mots ces classes permettent d’encapsuler un contrôle Windows Forms comme le canevas MFC le permet déjà avec un contrôle OLE. Cependant, pour offrir une intégration Windows Forms satisfaisante, l’équipe MFC a apporté des modifications qui dépassent l’ajout de quelques classes dédiées CWinFormsXXX. La classe CWnd (pilier de l’architecture MFC) a subie quelques aménagements, en offrant une nouvelle déclinaison de la méthode CreateControl.

//Overload for special controls (WinForms), that require more than CLSID.
BOOL CreateControl(const CControlCreationInfo& creationInfo, DWORD dwStyle,
const
POINT* ppt, const SIZE* psize, CWnd* pParentWnd, UINT nID);

Le premier paramètre de cette méthode, est un nouveau type CControlCreationInfo qui permet de véhiculer à la fois des informations COM et .NET nécessaire aux contrôles Windows Forms.

De ce fait, vous devrez ajouter dans votre projet MFC (si ce n’est pas déjà le cas) AfxEnableControlContainer dans la méthode InitInstance de la classe dérivée CWinApp. De toute façon si vous oubliez cet ajout, le compilateur vous le signalera :

Warning: AfxEnableControlContainer has not been called yet. >>> You should call it in your app's InitInstance function.CWinApp.

Remarque : Vous ne devriez pas être totalement étranger à la notion de réutilisation des contrôles Windows Forms hébergés dans des conteneurs hétérogènes. Rappelez vous, Windows Forms 1.x permettait déjà encapsuler un contrôle utilisateur dans une page Web ASP.Enfin, mais déjà plus confidentiel (et surtout beaucoup plus technique), la capacité d’héberger un contrôle Windows Forms, dans une instance la classe CFormView ou une Boîte de dialogue MFC, encapsulé dans la méthode COleControlSite.

Pour conclure cette préparation, nous allons ajouter au projet Scribble, une référence en cliquant dans la fenêtre Solution sur le projet Scribble, en cliquant droit et sélectionnant : References ... .

Figure 8 : sélection de l’item References… pour le projet Scribble C++ (/clr)

Vous obtenez la boîte de dialogue des propriétés du projet Scribble sélectionnée sur les références. Vous pouvez alors ajouter la référence du contrôle Windows Forms précédemment créé en cliquant sur Add New Reference…, puis en choisissant dans l’onglet Projects : ScribbleUserControl.


Figure 9 : ajout d’une référence sur le contrôle ScribbleUserControl au projet Scribble

Cette opération revient à ajouter sur la ligne de commande du compilateur C++/CLI l’option /FU<file> (forced using assembly/module). Si vous recompilez, le contrôle utilisateur ScribbleUserControl.dll est recopié dans le répertoire génération du projet Scribble.

Remarque : l’ordre #using <file> à inclure dans le code C++/CLI pour référencer des assemblages consommés par votre application n’est pas à utiliser si vous avez déjà référencé vos assemblages avec l’option /FU.

Un mot sur la conception des contrôles Windows Forms, pour garder une réutilisation maximum il ne faudrait pas ajouter des fonctionnalités spécifiques à l’application qui héberge le ou les contrôles Windows Forms. Donc, dans nos exemples, on se gardera bien de développer de l’intelligence « métier » côté contrôle Windows. Ainsi, tous les contrôles pourraient être réutilisés dans d’autres applications MFC ou .NET.

Contrôle Windows Forms hébergé dans une boîte de dialogue MFC

Motivation

Dans ce premier cas d’intégration, nous allons nous intéresser à l’hébergement d’un contrôle Windows Forms sur la surface d’une boîte de dialogue MFC à travers un petit cas concret. Dans l’application Scribble nous pourrions ajouter la fonctionnalité d’insérer des commentaires aux dessins représentés dans les vues de l’application. Sur un clic droit nous pourrions mémoriser le point d’impact de la souris et lancer une boite de dialogue pour saisir le texte du commentaire. Une fois un commentaire saisi, sur validation, le commentaire apparaît au point d’impact précèdent.

Développer un Contrôle Windows Forms

Pour saisir le commentaire nous pouvons réaliser une boîte de dialogue MFC contenant un contrôle Windows Forms contenant lui même un label et un champ de saisi. Nous allons donc créer un contrôle Windows Forms C# nommé UserControlComment et y ajouter un label et un TextBox comme affiché ci-dessous.

Figure 10 : le contrôle UserControlComment en mode Design

 

Le code généré dans le fichier UserControlComment.cs n’a rien de surprenant.

namespace ScribbleUserControl {

public partial class UserControlComment : UserControl {

public UserControlComment(){
 InitializeComponent();
 }
}

}

Le support du nouveau mot clef partial en C# 2.0 permet à l’assistant Visual Studio 2005 de scinder la définition des classes Windows Forms en deux fichiers et de palier ainsi au problème rencontré en Visual Studio 2002-2003, où le Designer pouvait altérer par erreur le code développé, car trop exposé. En répartissant la classe en deux fichiers le risque de perdre du code est diminué: un premier fichier destiné au développeur, UserControlComment.cs et un second fichier dédié au Designer pour gérer les contrôles graphiques et leurs propriétés, UserControlComment.Designer.cs.

namespace ScribbleUserControl {

partial class UserControlComment {

// code supprimé pour une meilleur visibilité

// …

private System.Windows.Forms.TextBox textBox1; 

public System.Windows.Forms.TextBox TextBox1
{
get
{ return textBox1; }
}

private System.Windows.Forms.Label labelComment;
}
}

Le contrôle TextBox est par défaut nommé textBox1 en accès privé. Cependant il nous faudra y accéder depuis le code C++ MFC. Vous avez le choix de le rendre public en changeant son mode d’accès, ou en l’encapsulant dans une propriété .NET publique.

Développer l’intégration MFC

Pour intégrer le contrôle UserControlComment dans une boîte de dialogue MFC, ajoutons une ressource au projet Scribble de type Dialog nommée IDD_COMMENT_DIALOG. Dans cette ressource de boîte de dialogue, nous avons ajouté un contrôle de Static Text nommé IDC_PLACE_HOLDER_STATIC (rectangle noir sur l’image ci-dessous). De plus, pour que notre contrôle utilisateur récupère le focus directement, il est important d’imposer le Z-Order en débutant sur le contrôle IDC_PLACE_HOLDER_STATIC puis sur le bouton IDOK.

Figure 10 : La ressource MFC IDD_COMMENT_DIALOG contenant le contrôle d’accueil IDC_PLACE_HOLDER_STATIC, pour le contrôle UserControlComment

On associe une classe dérivant de la classe CDialog à cette ressource que l’on nomme CDialogComment. Puis on utilise la classe d’intégration des contrôles Windows Forms, CWinFormsControl<TManagedUserControl>. Cette classe est destinée à héberger un contrôle Windows Forms dans un formulaire MFC ou dans une boîte de dialogue MFC.

Constructeur

CWinFormsControl::CWinFormsControl

Construit l’objet qui encapsule un contrôle Windows Forms.

Opérations

CWinFormsControl::CreateManagedControl

Créé un contrôle Windows Forms dans un conteneur MFC.

CWinFormsControl::GetControl

Récupère un handle C++/CLI sur le contrôle Windows Forms.

CWinFormsControl::GetControlHandle

Récupère un handle Windows sur le contrôle Windows Forms.

Opérateurs

CWinFormsControl::operator ->

Remplace CWinFormsControl::GetControl dans les expressions

CWinFormsControl::operator TManagedControl^

Convertit un type en un handle C++/CLI du type contrôle Windows Forms.

Pour plus d’information, je vous invite à ouvrir les fichiers afxWinForms.h et afxwinforms.inl.

Poursuivons nos travaux d’implémentation, en agrégeant une donnée membre nommée m_ctrlComment typée via la classe CWinFormsControl< UserControlComment> de portée privée à la classe CDialogComment.

class CDialogComment : public CDialog {
DECLARE_DYNAMIC(CDialogComment) // Data member for the .NET User Control:

CWinFormsControl<ScribbleUserControl::UserControlComment> m_ctrlComment;

CString m_strComment;
public
:
 CDialogComment(CWnd* pParent = NULL); // standard constructor
virtual
~CDialogComment();

// Dialog Data

enum { IDD = IDD_COMMENT_DIALOG }; 

protected:

virtual
void DoDataExchange(CDataExchange* pDX); // DDX/DDV support

DECLARE_MESSAGE_MAP()

}

 Côté implémentation, nous devons assurer l’association entre le contrôle Static Text nommé IDC_PLACE_HOLDER_STATIC, dont l’objectif est de définir l’espace où le contrôle Windows Forms viendra se positionner. La méthode DoDataExchange, assure traditionnellement l’échange bidirectionnel entre données stockées dans les contrôles et les variables C++ associées. Dans le cadre de l’intégration des Windows Forms, une nouvelle méthode, DDX_ManagedControl, permet d’édifier la correspondance entre le contrôle statique et le contrôle Windows Forms encapsulé dans la classe CWinFormsControl. Mais en interne le contrôle Windows Forms est créé au sein de la méthode DDX_ManagedControl via la méthode CWinFormsControl::CreateManagedControl().

void CDialogComment::DoDataExchange(CDataExchange* pDX) {

CDialog::DoDataExchange(pDX);

DDX_ManagedControl(pDX, IDC_PLACE_HOLDER_STATIC, m_ctrlComment);

if (pDX->m_bSaveAndValidate) {

m_strComment = m_ctrlComment->TextBox1->Text;

 } else {

m_ctrlComment->TextBox1->Text = gcnew System::String(m_strComment);

 }
}

En fonction du sens de l’échange on récupère le contenu du contrôle TextBox1 de notre contrôle Windows Forms dans une chaîne MFC/ATL CString ou l’inverse. On note ici qu’il n’y a pas de problème de transtypage .NET vers C++ ou C++ vers .NET. Nous sommes dans le cas où la conversion est assurée nativement. Si nous avions à échanger un type autre que scalaire « bittable » nous aurions du effectuer une conversion (marshaling de types).

En résumé

A ce stade, les travaux d’intégration sont terminés et les développements MFC / C++ complémentaires ne font pas l’objet de cet article.

Résumons rapidement les éléments essentiels du travail d’intégration:

ü      après avoir agrégée une instance de : CWinFormsControl<ScribbleUserControl::UserControlComment> dans une classe dérivée de CDialog,

ü      il suffit d’édifier une association sur un contrôle d’un contrôle fictif (juste pour définir la place qu’occupera le contrôle utilisateur Windows Forms) de la ressource dialogue associée via la méthode DDX_ManagedControl,

ü      puis de tenir compte du sens de l’instance de la classe CDataExchange, pour peupler ou obtenir le contenu d’un contrôle utilisateur Windows Forms.

Sur le plan de l’exécution, sur un clic droit sur la zone cliente de l’application Scribble, on obtient notre boîte de dialogue hébergeant le contrôle utilisateur ScribbleUserControl::UserControlComment :

Figure 11 : La boîte de dialogue pour saisir des commentaires en action

On note que le contrôle Windows Forms est parfaitement intégré. Pour l’utilisateur final, il semble bien difficile de déterminer si cette boîte de dialogue est native ou un mélange de natif et de code géré.

Figure 11 : l’application Scribble avec des dessins commentés

Contrôle Windows Forms comme une boîte de dialogue MFC

Motivation

Dans ce second cas d’intégration, nous allons nous intéresser à l’hébergement d’un contrôle Windows Forms utilisé comme une boîte de dialogue. Pour illustrer ce dernier cas, nous pourrions remplacer la boîte de dialogue About Scribble…  par un contrôle utilisateur représentant les mêmes informations.

Développer un Contrôle Windows Forms

Pour développer une nouvelle boîte de dialogue « About » Windows Forms, nous dévons réaliser un nouveau contrôle Windows Forms contenant les mêmes informations que la version MFC.

Figure 12 : fenêtre About MFC de l’application Scribble

Nous allons donc créer un contrôle Windows Forms C# nommé UserControlAbout et y ajouter deux labels de type Label, une image de type PictureBox (l’image provient d’une copie du site www.windowsforms.com) et enfin un bouton de type Button comme affiché sur la figure ci-dessus.

Figure 13 : contrôle About Windows Forms de l’application Scribble en mode Design

Le contrôle Button est par défaut nommé button1 en accès privé. Cependant il nous faudra y accéder depuis le code C++ MFC. Vous avez le choix de le rendre public en changeant son mode d’accès, ou en l’encapsulant dans une propriété .NET publique dans le fichier UserControlAbout.Designer.cs.

namespace ScribbleUserControl {

partial class UserControlAbout {

// code supprimé pour une meilleur visibilité

// …

private System.Windows.Forms.Label label1;
private
System.Windows.Forms.Label label2;
private
System.Windows.Forms.PictureBox pictureBox1;
private
System.Windows.Forms.Button button1;
 

public System.Windows.Forms.Button Button1 {
get
{ return button1; }
}

}
}

Développer l’intégration MFC

Pour intégrer le contrôle UserControlAbout comme une boîte de dialogue MFC, nous modifions la classe CAboutDlg qui par défaut dérive de la classe CDialog, en remplaçant cette dernière par la classe CWinFormsDialog<UserControlAbout>. Cette classe qui dérive de CDialog, est destinée à héberger un contrôle Windows Forms et permet d’afficher celui-ci comme une boîte dialogue modale ou modeless.

Constructeur

CWinFormsDialog

Construit l’objet CWinFormsDialog qui encapsule le contrôle Windows Forms

Opérations

GetControl

Récupère un handle C++/CLI sur le contrôle Windows Forms.

GetControlHandle

Récupère un handle Windows sur le contrôle Windows Forms.

OnInitDialog

Initialise la boîte de dialogue MFC en créant, puis maintenant un contrôle utilisateur Windows Forms.

Opérateurs

CWinFormsDialog::operator ->

Remplace CWinFormsDialog::GetControl dans les expressions.

CWinFormsDialog::operator TManagedControl^

Convertit un type en un handle C++/CLI du type du contrôle Windows Forms.

Pour plus d’information, je vous invite à ouvrir les fichiers afxWinForms.h et afxwinforms.inl.

Poursuivons nos travaux, et penchons nous sur l’implémentation de la méthode OnInitDialog et préparons la gestion « du clic » sur le bouton « Ok » de la boîte de dialogue. Le fichier afxWinForms.h nous met à disposition une nouvelle macro permettant de créer des délégués sur des évènements .NET depuis une application MFC : MAKE_DELEGATE.

BOOL CAboutDlg::OnInitDialog() {

CWinFormsDialog<ScribbleUserControl::UserControlAbout>::OnInitDialog();

GetControl()->Button1->Click += MAKE_DELEGATE(System::EventHandler, OnButton1);

this->SetWindowText("About Scribble"); 

return TRUE;
}

La définition de la macro MAKE_DELEGATE montre qu’une donnée membre m_delegate_map_proxy, doit être définie.

#define MAKE_DELEGATE(DELEGATE,MEMBER)\

gcnew DELEGATE(m_delegate_map_proxy.get_proxy(this),&delegate_proxy_type::MEMBER)

A l’instar de la gestion des messages Windows en MFC (BEGIN_MESSAGE_MAP/END_MESSAGE_MAP), on déclare une carte des délégués via deux macros : BEGIN_DELEGATE_MAP/END_DELEGATE_MAP. Chaque entrée est matérialisée par une macro, EVENT_DELEGATE_ENTRY, permettant de définir le nom du délégué et sa signature.

public:

BEGIN_DELEGATE_MAP( CAboutDlg )

EVENT_DELEGATE_ENTRY( OnButton1, System::Object^, System::EventArgs^ );

END_DELEGATE_MAP()

Il ne nous reste plus qu’à implémenter la méthode supportant le délégué, permettant de traduire l’action d’un clic sur le bouton de la boîte de dialogue. On invoque l’implémentation par défaut MFC du bouton « Ok », qui ferme la boîte de dialogue.

void CAboutDlg::OnButton1( System::Object^ /*sender*/, System::EventArgs^ /*e*/ ) {
this
->OnOK();

}

En résumé

Résumons rapidement les éléments essentiels du travail d’intégration :

ü      après avoir substitué la classe dérivée (CDialog) par la classe CWinFormsDialog<UserControlAbout>,

ü      il suffit d’implémenter le traitement de l’évènement clique sur le bouton « OK » grâce la macro MAKE_DELEGATE dans la méthode d’initialisation OnInitDialog.

ü      Enfin une fois défini la carte des délégués, BEGIN_DELEGATE_MAP/END_DELEGATE_MAP, ici une entrée,

ü      il ne suffit plus que d’implémenter la méthode supportant la définition du délégué.

Sur le plan de l’exécution, il suffit de cliquer sur le menu About, ou le bouton « ? » de l’application Scribble, pour obtenir notre boîte de dialogue hébergeant le contrôle utilisateur UserControlAbout.

Figure 14 : fenêtre About en Windows Forms en action dan l’application Scribble

La nouvelle boîte de dialogue « About Scribble » affichant sont identité et même sa nature. On note sur le plan de l’expérience utilisateur que cette boîte, mise à part son contenu, ne permet pas de distinguer un changement technologique.

Contrôle Windows Forms comme une vue MFC

Motivation

Dans ce troisième et dernier cas d’intégration, nous allons nous intéresser à l’hébergement d’un contrôle Windows Forms au sein d’une vue MFC à travers un dernier cas : dans notre application Scribble nous pourrions afficher la taille des points des strokes dans une grille Windows Forms. Cette grille devrait permette de modifiez la valeur de la taille des points et d’être reflétée dans la vue de dessin.

Développer un Contrôle Windows Forms

Pour afficher et saisir les tailles des points, nous allons donc créer un contrôle Windows Forms C# nommé UserControlGrid et ajouter sur la surface un contrôle de type DataGridView comme affiché sur la figure ci-dessous.

Figure 15 : contrôle DataGridView en mode Design pour l’affichage les tailles des pinceaux

Ce contrôle fait partie des grandes nouveautés de Windows Forms 2.0. Dans notre cas, nous n’illustrerons qu’une infime partie des capacités de ce contrôle. Comme nous souhaitons utiliser le modèle Document/Vue de l’application Scribble, nous utiliserons la grille en mode virtuel, c’est dire que celle-ci ne stocke pas les données en interne, mais elle les réclame du document, si le contrôle doit rafraichir son affichage, et les repousse vers le document, si un utilisateur modifie des valeurs (dans notre cas l’utilisateur n’a pas à créer de nouvelles lignes via la grille).

this.dataGridView.VirtualMode = true;

Comme dans le cas précèdent, nous aurons besoin d’appeler un objet agrégé dans le contrôle utilisateur Windows Forms, ici dataGridView, depuis le code C++ / MFC qui est par défaut exposé en privé.

private System.Windows.Forms.DataGridView dataGridView; 

public System.Windows.Forms.DataGridView DataGridView{

get { return dataGridView; }
}

 Comme dans le cas précèdent, nous encapsulons le champ dans une propriété publique.

Développer l’intégration MFC

Nous devons créer une nouvelle classe, nommée CScribbleGribView dérivant de CView en utilisant l’assistant de l’IDE. On remplace le type CView par la classe d’intégration CWinFormsView à la fois dans le fichier en-tête et dans le fichier d’implémentation. La motivation de la classe CWinFormsView  est d’héberger un contrôle Windows Forms comme une vue MFC.

Constructeur

CWinFormsView::CWinFormsView

Construit un objet CWinFormsView.

Opérations

CWinFormsView::GetControl

Récupère un handle C++/CLI sur le contrôle Windows Forms.

Opérateurs

CWinFormsView::operator Control^

Convertit un type en un handle C++/CLI du type du contrôle Windows Forms.

 Pour plus d’information, je vous invite à ouvrir les fichiers afxWinForms.h et afxwinforms.inl.

 Reprenons nos travaux d’intégration. Nous avons surchargé la méthode GetControl de manière à retourner un type ScribbleUserControl ::UserControlGrid^ au lieu du type System::Windows::Forms::Control^ 

class CScribbleGridView : public CWinFormsView {

DECLARE_DYNCREATE(CScribbleGridView)

public:

ScribbleUserControl::UserControlGrid^ GetControl();

 Dans le fichier d’implémentation le constructeur CWinFormsView réclame en paramètre l’identifiant (typeid) du type du contrôle Windows Forms.

CScribbleGridView::CScribbleGridView() : CWinFormsView(ScribbleUserControl::UserControlGrid::typeid){}

A ce stade, nous n’avons pas associé les données contenues dans le document MFC à la grille Windows Forms. Dans la méthode OnInitialUpdate de la class CScribbleGridView, nous récupérons une référence pour associer deux délégués .NET respectivement aux évènements exposés par la grille, CellValuePushed qui repousse les données modifiées et CellValueNeed qui réclame les données à afficher dans la grille. Enfin nous appelons la méthode Add() sur le contrôle DataGridView autant de fois qu’il y a d’éléments dans la liste de strokes, de manière à remplir la grille. Enfin, nous ajouterons un troisième délégué pour répondre au traitement de validation de saisie dans la grille via l’évènement CellValidating. En effet la taille des points ne peut être qu’un entier positif.

Remarque : pour découvrir la signature des délégués vis-à-vis des évènements sélectionnés, vous devez vous référer à la documentation MSDN, ou bien d’utiliser l’Object Browser de l’IDE Visual Studio 2005.

void CScribbleGridView::OnInitialUpdate(){

CWinFormsView::OnInitialUpdate();
UserControlGrid^ pUserCtrl = GetControl();

if (pUserCtrl != nullptr){
  pUserCtrl->DataGridView->CellValuePushed += MAKE_DELEGATE(DataGridViewCellValueEventHandler, OnCellValuePushed);
  pUserCtrl->DataGridView->CellValueNeeded += MAKE_DELEGATE(DataGridViewCellValueEventHandler, OnCellValueNeeded);
 
pUserCtrl->DataGridView->CellValidating += MAKE_DELEGATE(DataGridViewCellValidatingEventHandler, OnCellValidating);

  CTypedPtrList<CObList,CStroke*>& strokeList = this->GetDocument()->m_strokeList;

 POSITION pos = strokeList.GetHeadPosition();

 for (int idx = 0; pos != NULL; idx++){
 
strokeList.GetNext(pos);
  DataGridView^ dataGridView = pUserCtrl->DataGridView;
  dataGridView->Rows->Add();
 
}
}
}

 Dans le fichier d’entête ScribbleGribView.h, nous devons déclarer une section relative aux délégués gérés par la classe, le tout dans un « esprit très MFC ». Chaque entrée est matérialisée par la macro permettant de définir le nom du délégué et sa signature. Enfin, nous déclarons les méthodes correspondantes aux supports des délégués déclarés précédemment.

BEGIN_DELEGATE_MAP( CScribbleGridView )
EVENT_DELEGATE_ENTRY(OnCellValuePushed, Object^, DataGridViewCellValueEventArgs ^)
EVENT_DELEGATE_ENTRY(OnCellValueNeeded, Object^, DataGridViewCellValueEventArgs ^)

EVENT_DELEGATE_ENTRY(OnCellValidating, System::Object^, DataGridViewCellValidatingEventArgs ^)

END_DELEGATE_MAP() 

afx_msg void OnCellValuePushed(Object^ sender, DataGridViewCellValueEventArgs ^);

afx_msg void OnCellValueNeeded(Object^ sender, DataGridViewCellValueEventArgs ^);

afx_msg void OnCellValidating(Object^ sender, DataGridViewCellValidatingEventArgs ^);

Nous pouvons maintenant implémenter ces trois méthodes, OnCellValuePushed, OnCellValueNeed, et OnCellValidating dans le fichier d’implémentation.

void CScribbleGridView::OnCellValuePushed(Object^ /*pSender*/, DataGridViewCellValueEventArgs^ e) {

CScribbleDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
CStroke* pStroke; 

if ((pStroke = GetStroke(e->RowIndex)) != NULL){

ASSERT_VALID(pStroke);
pStroke->SetPenWidth(Int32::Parse(e->Value->ToString()));
pDoc->UpdateAllViews(this, 0L, NULL);
pDoc->NotifyChanged();
}
}

void CScribbleGridView::OnCellValueNeeded(Object^ /*sender*/, DataGridViewCellValueEventArgs^ e) {

CStroke* pStroke; 

if ((pStroke = GetStroke(e->RowIndex)) != NULL) {
  e->Value = pStroke->GetPenWidth();
 }
}

void CScribbleGridView::OnCellValidating(Object^ /*sender*/,DataGridViewCellValidatingEventArgs ^e) {

ScribbleUserControl::UserControlGrid^ pUserCtrl = GetControl();

if (pUserCtrl != nullptr) {

 pUserCtrl->DataGridView->Rows[e->RowIndex]->ErrorText = "";
 System::Int32 newInteger;

 if (!System::Int32::TryParse(e->FormattedValue->ToString(), newInteger) || newInteger <= 0) {

    e->Cancel = true;
    pUserCtrl->DataGridView->Rows[e->RowIndex]->ErrorText = "the value must be a positive integer";
 
}
}
}

A ce stade nous avons réglé la phase d’initialisation de la grille vis à vis du contenu du document, mais aussi la saisie d’une modification de valeur dans la grille Windows Forms. Cependant nous devons gérer aussi le cas d’un nouveau stroke de points dans la vue de dessin, qui notifie via méthode CScribbleGridView::OnUpdate().

void CScribbleGridView::OnUpdate(CView* /*pSender*/, LPARAM /*lHint*/, CObject* pHint) {

if (pHint != NULL) {

 if (pHint->IsKindOf(RUNTIME_CLASS(CStroke))) {

  // The hint is that a stroke as been added (or changed).
  UserControlGrid^ pUserCtrl = GetControl();

  if (pUserCtrl != nullptr) {
    DataGridView^ dataGridView = pUserCtrl->DataGridView;
    dataGridView->Rows->Add();

  }
 }
}
}

On constate que l’intégration au modèle Document/Vue ne pose aucun problème, et que le mode virtuel du contrôle DataGridView s’adapte parfaitement à notre besoin. Cependant reconnaissons que notre besoin est fort simple et que dans le cas de données non « bittable » (ici nous traitions un entier) un effort de marshalling serait systématique.

En résumé

Résumons brièvement les principaux éléments du travail d’intégration :

ü      après avoir dérivé votre classe vue de la classe CWinFormsView,

ü      nous avons passé au constructeur de cette même classe l’identifiant du contrôle utilisateur Windows Forms hébergé.

ü      Puis dans la méthode d’initialisation des vues OnInitialUpdate, on a associé trois évènements, deux pour gérer les données de la grille .NET CellValuePushed et CellValueNeed et un troisième pour gérer la validité de la saisie dans la grille, CellValidating.

ü      Après avoir déclaré et implémenté ces délégués, nous avons au sein de la méthode OnUpdate ajouté le code de notification d’ajout d’un stroke.

Figure 16 : le modèle document/vue en action entre une vue d’intégration.des Windows Forms et une vue MFC

On note, comme dans les cas précédents, que le contrôle utilisateur Windows Forms est parfaitement intégré. Pour l’utilisateur final, il semble bien difficile de déterminer si la vue de gauche du splitter est native ou un mélange de natif et de code géré.

MFC Interfaces Library

Motivation

La distribution Visual Studio 2005 arrive avec un assemblage utilitaire, mfcmifc80.dll pour MFC Managed Interfaces Library. Cet assemblage (pure .NET) ne contient que des interfaces dédiées à l’intégration des contrôles Windows Forms des classes MFC. L’ensemble des éléments contenus dans cet assemblage est rangé dans l’espace de nom Microsoft.VisualC.MFC.

Figure 17 : affichage des types contenu dans l’assemblage MFC Managed Interface Library

En réalité on distingue dans ces éléments deux catégories de services :

  • Gestion de messages de type commande (WM_COMMAND), repoussés par la classe CWinFormsView.
  • Gestion l’interface IView, stimulée directement par la classe CWinFormsView.

Gestion des messages de type commande

Introduction

Pour introduire la gestion des messages de commande, nous débuterons par l’interface ICommandTarget. L’interface ICommandTarget est relative aux messages de type commande (WM_COMMAND) traités par le canevas MFC (CFrameWnd::OnCmdMsg), elle permet aux contrôles utilisateurs Windows Forms de proposer une gestion personnalisée de ces commandes, via l’unique méthode Initialize.

ICommandTarget::Initialize

Initialise l’objet command target.

Cette méthode prend en paramètre l’interface ICommandSource, que la méthode ICommandTarget ::Initialize est censée initialiser. L’objectif est d’offrir une opportunité au contrôle utilisateur Windows Forms d’enregistrer des gestionnaires de commandes de menus, de manière à pouvoir intercepter, désactiver, cocher des menus de l’application MFC. 

ICommandSource::AddCommandHandler

Ajoute un gestionnaire de commande à l’objet source de commandes.

ICommandSource::RemoveCommandHandler

Retire un gestionnaire de commande à l’objet source de commandes.

ICommandSource::AddCommandUIHandler

Ajoute un gestionnaire de commande interface utilisateur à l’objet source de commandes.

ICommandSource::RemoveCommandUIHandler

Retire un gestionnaire de commandes d’interface utilisateur.

ICommandSource::AddCommandRangeHandler

Ajoute un groupe de gestionnaires de commandes à l’objet source de source de commandes.

ICommandSource::RemoveCommandRangeHandler

Retire un groupe de gestionnaires de commandes à l’objet source de source de commandes.

ICommandSource::AddCommandRangeUIHandler

Ajoute un groupe de gestionnaires de commandes interface utilisateur à l’objet source de source de commandes

ICommandSource::RemoveCommandRangeUIHandler

Retire un groupe de gestionnaires de commandes interface utilisateur à l’objet source de source de commandes

ICommandSource::PostCommand

Poste un message de commande (WM_COMMAND) sans attendre qu’il soit traité.

ICommandSource::SendCommand

Envoie un message de commande (WM_COMMAND) et attend qu’il soit traité, avant de retourner.

Exemple d’utilisation

Revenons à l’exemple précédent, côté contrôle Windows Forms pour illustrer l’usage ICommandTarget. Cette fois nous avons ajouté en référence l’assemblage mfcmifc80.dll à notre projet ScribbleUserControl de manière à implémenter l’interface ICommandTarget. Ici nous avons voulu traiter le menu Pen Widths… de l’application Scribble.

Figure 18 : affichage du menu pinceau dans l’application Scribble

Nous avons recopié des ressources MFC, le numéro d’identifiant du menu, ici 32773 (nommé ID_PEN_WIDTHS) en champ privé de notre classe.

namespace ScribbleUserControl {

public partial class UserControlGrid : UserControl, ICommandTarget {

private const int ID_PEN_WIDTHS = 32773;
private
ICommandSource cmdSource;

private void PenWidthMenuHandler(uint cmdUI) {

 DialogResult res = MessageBox.Show("Would you like disabled \"Pen Widths Menu\" ?", "From Windows Forms 2.0", MessageBoxButtons.YesNo);

 if (res == DialogResult.Yes) {
    this
.cmdSource.AddCommandUIHandler(ID_PEN_WIDTHS, new CommandUIHandler(PenWidthsMenuUIHandlerHandler));
 }
 else
{
    this
.cmdSource.RemoveCommandHandler(ID_PEN_WIDTHS);
 }
}

private void PenWidthsMenuUIHandlerHandler(uint cmdUI, ICommandUI commandUI) {
  commandUI.Enabled = false;

public void Initialize(ICommandSource cmdSource) {

  this.cmdSource = cmdSource;
  this
.cmdSource.AddCommandHandler(ID_PEN_WIDTHS, new CommandHandler(PenWidthMenuHandler));
}

Dans notre la méthode Initialize, nous capturons la commande ID_PEN_WIDTHS en y associant un gestionnaire, PenWidthMenuHandler et au sein de l’implémentation de ce gestionnaire on ajoute l’abonnement d’un gestionnaire type commande interface utilisateur via une demande proposée par une boîte de message. Si la boîte de message est validée, la commande ID_PEN_WIDTHS est desactivée, sinon le gestionnaire PenWidthsMenuHandlerHandler est retiré

Figure 19 : affichage du menu pinceau dans l’application Scribble et son sous menu désactivé par le contrôle Windows Forms

Zoom sur l’implémentation de l’intégration .NET

L’objectif est de montrer que finalement, l’intégration des messages de commande dans le code MFC est très simple à comprendre. Sur la création de l’objet vue, après avoir créé le contrôle Windows Forms (CreateManagedControl), on interroge si le contrôle Windows Forms courant, implémente l’interface ICommandTarget. Si oui, un objet CCommandSource est instancié, puis passé en paramètre de la méthode Initialize de l’interface ICommandTarget.

BOOL CWinFormsView::Create(LPCTSTR lpszClassName,

LPCTSTR lpszWindowName, DWORD dwStyle,
const
RECT& rect,CWnd* pParentWnd, UINT nID,CCreateContext* pContext) {

m_nFlags |= WF_ISWINFORMSVIEWWND;
BOOL ok=__super::Create(lpszClassName, lpszWindowName, dwStyle, rect,pParentWnd, nID, pContext);
ASSERT(ok);
ok=ok && m_control.CreateManagedControl(m_pManagedViewType,WS_VISIBLE, rect, this,nID);
ASSERT(ok);
ICommandTarget^ pICmdTarget = dynamic_cast<ICommandTarget^>(GetControl());
  if
(pICmdTarget)  {
   CCommandSource^ p=gcnew CCommandSource;
   m_CmdSource = p;
   pICmdTarget->Initialize(p);
}

return ok;
}

Au regard de cet exemple de code, je vous laisse imaginer des implémentations d’interfaces plus personnelles.

Gestion de l’interface IView

Introduction

L’interface IView est clairement destinée à supporter des notifications des méthodes relatives à la classe CWinFormsView. Comme toute les vues du modèle Document/Vue cette classe supporte les méthodes OnActivateView, OnInitialUpdate, OnUpdate

IView::OnActivateView

Appelé par la classe CWinFormsView lorsqu’une vue est activée ou désactivée.

IView::OnInitialUpdate

Appelé par la classe CWinFormsView après que la vue a été attachée au document et avant d’être affichée une première fois.

IView::OnUpdate

Appelé par la classe CWinFormsView après que le document a été modifié par une autre vue, cette méthode permet à la vue de se mettre à jour afin de refléter les modifications.

Si on se penche sur la méthode OnUpdate qui est sensée fournir quelques informations, comme l’émetteur du message, sa nature et son contenu (pattern Observer). Ici, l’interface IView fournit une méthode qui ne prend aucun paramètre, donc il n’y à pas de moyen pour déterminer dans le contrôle Windows Forms d’où vient la demande de mise à jour, et quelle partie du document elle doit rafraîchir dans la vue, sachant que la classe CDocument n’est pas accessible, car celle-ci est native ? Bref, beaucoup de questions, qui me laissent un peu perplexe sur cette interface.

Exemple d’utilisation

Dans notre exemple, nous n’avons que le contrôle DataGridView à gérer au sein du contrôle utilisateur UserControlGrid. Comme ici la grille supporte le mode virtuelle, l’implémentation est simple.

namespace ScribbleUserControl {

public partial class UserControlGrid : UserControl, IView, ICommandTarget {

public delegate void UpdateViewHandler(object sender, EventArgs args);
public
event UpdateViewHandler OnUpdateView;

public void OnActivateView(bool activate){}

public void OnInitialUpdate() {}

public void OnUpdate() { this.DataGridView.Rows.Add(); }

Les limitations de la méthode IView.OnUpdate, rend l’usage de l’interface IView moins triviale si nous devions accéder directement la classe CDocument. Mais ces contraintes peuvent être retournées vers l’appelant, c'est-à-dire la classe MFC dérivant de CWinFormsView. Pour cela, il suffit de réaliser un délégué UpdateViewHandler et son évènement associé OnUpdateView. Si le code MFC déclare au moins un abonnement à l’évènement, la méthode IView.OnUpdate déclenchera un appel côté MFC. Revenons par l’exemple précédent, côté contrôle Windows Forms.

namespace ScribbleUserControl {

public partial class UserControlGrid : UserControl, IView, ICommandTarget {

public delegate void UpdateViewHandler(object sender, EventArgs args);
public
event UpdateViewHandler OnUpdateView;

public void OnActivateView(bool activate) {}

public void OnInitialUpdate() {}

public void OnUpdate() {

if (OnUpdateView != null) {
  OnUpdateView(this, null);
}
}

Côté MFC, au sein de la classe CScribbleGridView dérivant de la classe CWinFormsView, nous avons ajouté l’abonnement à l’évènement OnUpdateView.

void CScribbleGridView::OnInitialUpdate() {
CWinFormsView::OnInitialUpdate(); 

ScribbleUserControl::UserControlGrid^ pUserCtrl = GetControl();

if (pUserCtrl != nullptr) {

pUserCtrl->OnUpdateView += MAKE_DELEGATE(UserControlGrid::UpdateViewHandler, OnUpdateView);

 Puis implémenter la méthode supportant le délégué UserControlGrid::UpdateViewHandler. Ici, nous pourrions accéder à la classe CDocument, et récupérer les données nécessaires.

void CScribbleGridView::OnUpdateView(System::Object^ sender, System::EventArgs^ /*args*/) {

// The hint is that a stroke as been added (or changed).

if (sender != nullptr) {
ScribbleUserControl::UserControlGrid^ pUserCtrl = dynamic_cast<ScribbleUserControl::UserControlGrid^>(sender);
 

if (pUserCtrl != nullptr) {
// Possible, mais pas utile ici
// CScribbleDoc pDoc = GetDocument();

DataGridView^ dataGridView = pUserCtrl->DataGridView;
dataGridView->Rows->Add();
}
}
}

Remarque : dans cette version il n’est plus utile de surcharger la méthode CWinFormsView::OnUpdate.

Je reconnais vivement l’intérêt très limité de cette solution, mais ceci illustre la pauvre utilité de l’interface IView.

En résumé

Les deux services exposés par l’assemblage MFC Managed Interfaces Library ne proposent pas un avantage significatif vis-à-vis de l’intégration .NET. Le cas de l’interface IView reste pour moi une erreur d’implémentation. Cet assemblage s’inscrit comme un complément technique pour l’intégration des Windows Forms dans une application MFC, dont l’intérêt doit être évalué avant d’être utilisé.

Conclusion

L’intégration visuelle des contrôles Windows Forms dans une application MFC, offre un rendu magnifique, car parfaitement en accord avec les fenêtres MFC. Donc pas de crainte que l’utilisateur ne comprenne pas la provenance de fenêtres contenant du code Windows Forms.

 

Figure 20 : Contrôles utilisateurs Windows Forms intégrés à l’application Scribble

Sur le plan de la mise œuvre technique, les exemples précédents nous ont permis de constater que pour un développeur MFC ayant quelques connaissances sur la plateforme .NET, l’intégration .NET dans une application MFC ne relève pas d’une grande difficulté. Après avoir pris connaissance des classes d’intégration et de quelques nouvelles macros, la réalisation du travail d’intégration reste simple. Le choix de réaliser des écrans en Windows Forms intégrés à des applications MFC est donc un choix pertinent vis-à-vis d’un chemin de migration progressif vers la plateforme .NET.

Le tableau ci-dessous synthétise les éléments essentiels en fonction du type d’intégration.

Cas d’intégration

Classe d’intégration MFC

Contrôle Création /Association MFC

Mécanisme d’échange de données MFC / Contrôle WinForms

Contrôle

CWinFormControl<TManagedControl>

Agrégation, DDX_ManagedControl

CDataExchange, C++/CLI

Boîte de Dialogue

CWinFormDialog<TManagedControl >

Héritage, OnInitDialog

MAKE_DELEGATE, C++/CLI

Vue

CWinFormView(CtrlManaged ::typeid)

Héritage, OnInitialUpdate

MAKE_DELEGATE, C++/CLI

Notons enfin le travail remarquable des équipes MFC et C++/CLI qui ont largement contribuées à cette sensation de simplicité. Cependant on regrettera le pauvre intérêt de l’interface Microsoft.VisualC.MFC.IView qui n’apporte pas réellement de valeur ajoutée car vis-à-vis du modèle Document/Vue. Sur le plan des évolutions des MFC vis-à-vis de l’intégration avec les interfaces graphiques .NET, on parle déjà chez Microsoft de l’intégration MFC avec le futur canevas graphique vectoriel, nommée Windows Presentation Foundation (WPF) aux futures classes MFC 9.0 ; tout un programme !

Remerciements

J’aimerai remercier Bob Powell http://www.bobpowell.net/ qui est un « guru » .NET (Windows Forms & GDI+) et anciennement « Director of Software Engineering » chez Stingray divison de Rogue Wave http://www.roguewave.com/products/stingray/, pour la pertinence de ces remarques durant la réalisation de cet article.

Enfin, un grand merci aux deux compères C++ de Microsoft France DTE : Eric Mittelette http://blogs.microsoft.fr/ericmitt/ (le premier à avoir écrit un article en français sur l’intégration des Windows FormsMFC) et Eric Vernier http://blogs.microsoft.fr/ericv/ qui ont réalisé un tour de « force » C++, en « évangélisant » dans toute la France