Contents
In an e-mail to my first article "Hosting a MFC MDI application from within WinForms and WPF applications" I received a question - "Is it possible to build a container of MFC applications using the described technology?" This task has caused a little change of architecture to make components more flexible. Such work always brings pleasure, and it was the main motivation for this article. On the other hand, the result looks like an OLE container, but I have done this without creating COM objects!
I used two applications as samples: UmlEditor (MDI) and DialogEditor (SDI) by Johan Rosengren. We can simply embed both these applications in WinForms or the WPF framework, using MfcAdapter. For simplicity, I used RichTextBox based demo editors "FormsDemo" (WinFroms) and "WPFDemo" (WPF) as such kind of a container:
The main change is to divide MfcAdapter and MFC application in separate components. In this case, we will have an MfcAdapter that is fully independent from an actual MFC application:
The low level .NET component in the described architecture is a Mixed (managed/unmanaged) DLL with an MFC application and the managed class ModuleState inside.
The ModuleState is a managed pointer to unmanaged AFX_MODULE_STATE of the MFC application. With static method ModuleState.GetModuleState() the MfcAdapter or other C++/CLI component can load AFX_MODULE_STATE from necessary Mixed DLL. After this MfcAdapter can switch to this AFX_MODULE_STATE and use the unmanaged API of the hosted MFC application. Additionally, the Mixed MFC DLL provides managed equivalents to CommandIDs which can be processed in the MFC application.
To host MFC application in WinForms/WPF framework, as described in my first article, we should:
- Keep an invisible
CMainFrame and a created CView. I have updated the application initialization and set bMakeVisible parameter to "false" in the overridden document template to implement this. The good news is we don't need to override CDocManager and to change document like in previous version of MfcAdapter.
- Keep the modal properties of the hosted dialogs. Create the MFC MainFrame window as child of Framework MainForm window. Use the
CNotifyHook in the MFC MainFrame for supporting modal MFC dialogs in external framework.
- Auto-layout support. We have to implement some methods from
CLayoutView class in our hosted view: SetPrefferedSize(), GetPreferredSize(), and Scale().
The detailed description of necessary MFC code changes and Project settings necessary for creating mixed DLL are described in the previous article.
The mid-level component's MfcAppAdapter, ViewCtrl and ViewFrameworkElemen are wrappers to the unmanaged MFC application. These components combine into MfcAdapter.
MfcAppAdapter is responsible for the initialization/termination of the encapsulated MFC application, the command interface to the application and the document opening. The MFC View created in hosted MFC application can be found with method MfcAppAdapter.GetCreatedView() and can be hosted in ViewCtrl (WinForms) or ViewFrameworkElement (WPF). Mid-level components are not dependant on the actual MFC application or on the actual .NET/WPF Framework. The sample version supports SDI/MDI MFC applications, but it is possible to add support for other MFC application types: Dialog based and Multiple top-level documents.
The high level components are FormsDemo (WinForms) and WPFDemo (WPF). Both editors have two hosted MFC applications with documents: UmlEditor.dll + UmlEdi1.uml and DialogEditor.dll + DlgEdi1.dlg. Both editors support:
- Simply layout of hosted
Controls (FormsDemo) or hosted FrameworkElements (WPFDemo)
UpdateUI and event handling of the ToolStrip (FormsDemo) or ToolBar (WPFDemo) in hosted MFC applications.
While the initialization of MFC application and the hosting of MFC View is solved relatively easily, Layout is one of the main problems of the integration MFC and WinForms/WPF. To support necessary Layout in a hosted View, the CView interface is not enough, therefore I used the CLayoutView interface. With this interface, I realized a relatively simple Layout in a described example. If you need another layout realization, you can override methods: ViewCtrl. GetPrefferedSize(), ViewCtrl.ScaleControl() and use another interface.
Layout problems are also problems with WindowsFormsHost class. This class tries to realize the WPF Layout for hosted WinForms Control. Unfortunately, I could not take advantage of WindowsFormsHost even in simple cases. Is this the result of pure compatibility of WinForms and WPF Layout or simply programming bugs, I don't know. For example:
<!---->
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms">
<FlowDocumentPageViewer>
<FlowDocument TextAlignment="Left" Background="AliceBlue" >
<Paragraph>
<Bold>Test WindowsFormsHost</Bold>
</Paragraph>
<Paragraph>
TestCase1: change Zoom.
</Paragraph>
<Paragraph>
TestCase2: remove Background="Yellow" attribute from wrapped button.
</Paragraph>
<Paragraph>
<Button>WPF button</Button>
</Paragraph>
<Paragraph>
<WindowsFormsHost Background="Yellow">
<wf:Button Text="Windows Forms button" FlatStyle="Flat"/>
</WindowsFormsHost>
</Paragraph>
</FlowDocument>
</FlowDocumentPageViewer>
</Page>
The first Test Case would introduce a cycle, the second - exception.
Thus, instead of ViewCtrl and WindowsFormsHost classes, I used ViewFrameworkElemen class for hosting View in WPF. In this case, we should implement the necessary WPF Layout instead of implementation of WinForms Layout and its "conversion" to WPF with WindowsFormsHost. As well as in case of ViewCtrl we can override methods: ViewFrameworkElement. OnMeseareOverride() and ViewFrameworkElement.ArrangeOverride() to realize any other Layout, for example, to support the resolution-independent layout.
The current version of the MfcAdapter supports the hosting of CView or CScrollView based customers views. You can directly embed a control object (like CListCtrl, CHeaderCtrl and so on+) or for example COleDropTarget as class member in your class. In any case, don't use CView::OnCreate for creating and initializing of view class' members inherited from CWnd. This code should be removed from the OnCreate method to the OnInitialUpdate. In this case, these members will be created after the view attached to the WinForms Control or to HwndHost element in WPF.
The current version of the MfcAdapter does not support the hosting of CFormView based views.
CCtrlView, CListView, CTreeView, CEditView, CRichEditView are supported in the FULL version of the MfcAdapter only.
Demo Samples
The demo projects are comprised of .NET 2.0 projects:
- FormsDemo.exe - WinForms editor. Container with hosted MFC applications
UmlEditor and DialogEditor
- MdiFormsDemo.exe - WinForms editor. Full version of the hosted
UmlEditor
- SdiFormsDemo.exe - WinForms editor. Full version of the hosted
DialogEditor
- FormTest.exe - Simple test for hosting the
CScrollView and the CListCtrl
and .NET 3.0 projects:
- WpfDemo.exe - WPF editor. Container with hosted MFC applications
UmlEditor and DialogEditor
- WpfPageDemo.exe - WPF
FlowDocument. Container with hosted MFC applications UmlEditor and DialogEditor
Simply run the necessary application.
Source Code
After we unzip, we have one solution - Samples.sln with all demo projects. The deployed version of MfcAdapter consists of MfcAppAdapter.dll, ViewControl.dll, ViewFrameworkElement.dll, HostApp.h, LayoutView.h and supports only Unicode/Debug_x86 configuration. Simply copy *.dll and *.h files from "MfcAdapter" directory of zip to "MfcAdapter" directory of samples. Please note that standard "Debug" directory is renamed to "Debug_x86".
Set the necessary project as the startup project, and then build and run.
I tried to keep the encapsulated MFC code as short as possible, so I have not fixed some of the native problems of this code, for example, compiler warnings, an unhandled exception when opening a recent file that does not exist and others.
Download and use MfcAdapter SDK documentation for hosting your MFC Application.
Programming Guide with Source Code
I updated the programming guide and source code of the wizards MFC/Dialog application "DialogWiz" for current version of the MfcAdapter.
- 27.01.2007 - Initial version
- 01.04.2007 - Common controls support