[FAQ VC++]Comment héberger des Windows Forms Controls dans des vues MFC?
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
1 2
|
class CMfcWinFormsHostView : public CMfcDataGridView |
11. Dans le fichier stdAfx de MfcWinFormsHost on référence le projet MfcWinFormsHostDll
Code:
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
http://pics.imagup.com/member4/12893...nFormsHost.jpg
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:
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à :D