Soient des composants .NET faisant partis du namespace System.Windows.Forms comme une listview .NET ou un webbrowser .NET ou un DataGridView .NET ou un ReportViewer .NET et montrons comment nous pouvons les héberger dans une vue MFC dans un modèle SDI ou MDI.

Créeons une solution MFC Hosting contenant trois projets :

1. Un Projet C++/CLI HostingWinFormsControls Windows Forms Control Library
2. Un Projet DLL MfcWinFormsHostDll Dll d'extension MFC
3. Un Projet Exe MfcWinFormsHost Application MFC MDI

Dans le projet HostingWinFormsControls nous allons ajouter plusieurs user control pour chacun des composants .NET en oubliant pas de faire Dock in Parent Container de façon à remplir la surface de chacun de nos User Control

Dans le projet MfcWinFormsHostDll nous allons montrer comment une dll d'extension MFC peut hoster des contrôles .NET et illustrer ainsi l'exportation des classes C++ dans une application MFC

Le projet MfcWinFormsHost peut hoster les contrôles .NET de 2 façons. Soient par sa dll d'extension MfcWinFormsHostDll ou directement à partir du User Control HostingWinFormsControls.

Pour le cas d'un DataGridView le code généré est le suivant :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public ref class HostingWinFormsControlsControl : public System::Windows::Forms::UserControl
	{
	public:
		HostingWinFormsControlsControl(void)
		{
			InitializeComponent();
			//
			//TODO: Add the constructor code here
			//
		}

	// ...
	private: System::Windows::Forms::DataGridView^  dataGridView1;
	// ...
Maintenant il est question que ce usercontrol soit connecté à l'infrastructure des MFC. Nous allons entamer les étapes suivantes.

Pour le projet HostingWinFormsControls
1. Modifier la classe de base de chacun de nos user control comme ceci
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
	public ref class HostingWinFormsControlsControl : public System::Windows::Forms::UserControl
				, public Microsoft::VisualC::MFC::ICommandTarget 
				, public Microsoft::VisualC::MFC::IView
	{
2. Implémenter les méthodes virtualles commec cela
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 
private:
		Microsoft::VisualC::MFC::ICommandSource^ m_CmdSrc; // need ID of control in MFC dialog and callback function
 
	public:
		virtual void Initialize(Microsoft::VisualC::MFC::ICommandSource^ cmdSrc)
		{
                                      // Pour connecter les messages command avec votre CWinFormsViews
		   	m_CmdSrc = cmdSrc;
			//m_CmdSrc->AddCommandHandler(ID_COMMAND1, gcnew CommandHandler(this, &WpfUserControlHostControl::OnMethod1));
			//m_CmdSrc->AddCommandHandler(ID_COMMAND2, gcnew CommandHandler(this, &WpfUserControlHostControl::OnMethod2));
			//m_CmdSrc->AddCommandHandler(ID_COMMAND3, gcnew CommandHandler(this, &WpfUserControlHostControl::OnMethod3));
		}
 
		virtual void OnInitialUpdate()
		{   
		   // Le OnInitialUpdate de CWinFormsView() appelle cette méthode ci
 
		}
 
		virtual void OnUpdate()
		{
                              // Le OnUpdate de CWinFormsView() appelle cette méthode ci
 
		}
 
		virtual void OnActivateView(bool activate)
		{
                                // Lors que votre CWinFormsView() est activée
		}
3. Il faut ensuite référencer l'assemblie MFCMIFC80.dll dans le projet HostingWinFormsControlsControl qui se trouve typiquement dans le répertoire d''installation de votre Visual Studio (VS 2010 chez moi ) soit $(VsInstallDir)VC\atlmfc\lib\MFCMIFC80.dll

Pour le projet MfcWinFormsHostDll
4. Ajouter une classe MFC qui dérive de CView en l'appelant CMfcDataGridView. Ensuite remplacer toutes les occurences de CView en CWinFormsView
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class AFX_EXT_CLASS CMfcDataGridView : public CWinFormsView
{
	DECLARE_DYNCREATE(CMfcDataGridView)

protected:
	CMfcDataGridView();           // protected constructor used by dynamic creation
	virtual ~CMfcDataGridView();

public:
	virtual void OnDraw(CDC* pDC);      // overridden to draw this view
#ifdef _DEBUG
	virtual void AssertValid() const;
#ifndef _WIN32_WCE
	virtual void Dump(CDumpContext& dc) const;
#endif
#endif

protected:
	DECLARE_MESSAGE_MAP()
};
N'oubliez de mettre la macro AFX_EXT_CLASS devant CMfcDataGridView car elle sera exportée depuis notre Dll d'extension MFC MfcWinFormsHostDll. La classe de base doit être la CWinFormsView.

5. Ajouter une petite méthode très utilitaire dans notre classe
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
class AFX_EXT_CLASS CMfcDataGridView : public CWinFormsView
{ 
            //...
            // Attributes
public:
	gcroot<HostingWinFormsControls::HostingWinFormsControlsControl^> GetDataGridControl() const;
-> L'idée ici est que la CListView a son GetListCtrl(), la CTreeView a son GetTreeCtrl(), et donc notre CMfcXXXView a son GetXXXCtrl()

-> Lorsque des données membres d'une classe sont définies, ou bien certains argument ou type de retour de méthodes, les classes natives imposent à ce que les membres managés soit vêtu en toute transparence par des gcroot<>.

6. Dans le fichier source de MfcDataGridView.cpp on a :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
CMfcDataGridView::CMfcDataGridView()
         :CWinFormsView(HostingWinFormsControls::HostingWinFormsControlsControl::typeid)
{

}
C'est dans le constructeur que l'on définit notre type de vue

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
gcroot<HostingWinFormsControls::HostingWinFormsControlsControl^> CMfcDataGridView::GetDataGridControl() const
{
	return static_cast<HostingWinFormsControls::HostingWinFormsControlsControl^>(CWinFormsView::GetControl());
}
Et voici l'implémentation de notre GetXXXControl()

7. Il faut référencer le projet HostingWinFormsControls dans MfcWinFormsHostDll
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
 
// dans le StdAfx.h de MfcWinFormsHostDll
// ...
 
#if defined(__cplusplus_cli)
 
#include <vcclr.h>
#include <afxwinforms.h>   // MFC Windows Forms support
 
#using <HostingWinFormsControls.dll>
#endif
8. Configurer votre dll d'extension pour qu'elle support le runtime .NET
[FAQ VC++]Comment ajuster les settings de ses projets MFC pour supporter des composants .NET.

9. Préparer votre dll afin que ses objets (fichier *.h, *.lib) soit correctement exportable et vue des autres modules.
[FAQ VC++]Comment exploiter efficacement l'exportation des classes C++
En final on doit avoir le fichier MfcWinFormsHostDll.h et la librairie MfcWinFormsHostDll.lib
Le fichier MfcWinFormsHostDll.h
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
#pragma once

#include "MfcDataGridView.h"
La sortie de la librairie MfcWinFormsHostDll.lib
Propriétés du projet MfcWinFormsHostDll > Configuration Properties > Linker > Advanced > Import Library : $(SolutionDir)$(Configuration)\$(TargetName).lib

Pour le projet MfcWinFormsHost
10. Pour la classe CMfcWinFormsHostView remplacer toutes les occurences de sa classe de base CView par CMfcDataGridView
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
 
class CMfcWinFormsHostView : public CMfcDataGridView
11. Dans le fichier stdAfx de MfcWinFormsHost on référence le projet MfcWinFormsHostDll
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
 
// le fichier stdAfx de MfcWinFormsHost
 
#if defined(__cplusplus_cli)
 
#include <vcclr.h>
#include <afxwinforms.h>   // MFC Windows Forms support
 
#using <HostingWinFormsControls.dll>
#endif
 
#include "..\MfcWinFormsHostDll\MfcWinFormsHostDll.h"
Puis Propriétés du projet MfcWinFormsHost > Configuration Properties > C/C++ > General > Resolve #using References : $(SolutionDir)$(Configuration)\

12. Il faut référencer la librairie MfcWinFormsHostDll.lib dans le projet MfcWinFormsHost

Propriétés du projet MfcWinFormsHost > Configuration Properties > Linker > Input > Additional Dependencies : MfcWinFormsHostDll.lib
Propriétiés du projet MfcWinFormsHost > Configuration Properties > VC++ Directories > Librairies Directories : on ajoute $(SolutionDir)$(Configuration)\;


Bon on compile toute la solution et on obtient une application comme celle-ci



Les DataGridView de DotNet sont pour l'instant vide. Il suffit de les remplir en fournissant par exemple le DataSource qui est attaché au control interne DataGridView.

Pour travailler avec votre control vous avez la méthode CMfcWinFormsHostView::GetDataGridControl() de la vue de votre exe car
CMfcWinFormsHostView dérive de CMfcDataGridView de votre dll MFC et CMfcDataGridView embarque son contrôle interne gcroot<HostingWinFormsControls::HostingWinFormsControlsControl^>

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
 
void CUneClassDuProjetMfcWinFormsHost::SomeMethod(CDocument* pDoc)
{
	POSITION pos = pDoc->GetFirstViewPosition();
	CMfcWinFormsHostView* pView = static_cast<CMfcWinFormsHostView*>(pDoc->GetNextView(pos));
	HostingWinFormsControls::HostingWinFormsControlsControl^ pUserControl = pView->GetDataGridControl();
 
	System::Windows::Forms::DataGridView^ pDataGridView = pUserControl->dataGridView1;
 
            // bon je travaille avec mon DataGridView
           // ....
}
Remarquez que pour obtenir ce résultat il suffit d'avoir bien maîtriser les 2 points ci dessous:
1. [FAQ VC++]Comment exploiter efficacement l'exportation des classes C++

2. [FAQ VC++]Comment ajuster les settings de ses projets MFC pour supporter des composants .NET.

Voilà