Nowadays still many MFC applications exist. And maybe there’s no reason to rewrite the application as MFC gets big improvements with Visual Studio 2010. With that in mind it would be great to integrate WPF controls within MFC. Of course that’s possible!
Interoperability between MFC and WPF is based on two major features:
- Managed and native code interop
- Window handles to WPF elements
Using managed and native code in a mixed way can be done with C++/CLI. C++/CLI allows using managed code from native classes and the other way around. Instead of extending the C++ language with keywords starting with __ (which was done with Managed C++), C++/CLI is a syntax that extends C++ with managed keywords such as ref to define a managed ref type, value to define a managed value type, and gcnew to allocate an object on the managed heap. The C++/CLI syntax was already discussed in some older blog entries that are referenced below, and some new ones will follow.
So the first step on MFC and WPF interop are
- Create a WPF library, e.g. with a user control
- Create a MFC application
- Add Common Language Runtime support to the MFC application (Configuration Properties -> General)
- Reference the necessary assemblies (Common Properties –> Framework and References)
- At least these should be: System, PresentationCore, PresentationFramework, WindowsBase, and of course the assembly holding the user control.
Interoperability with WPF is done by creating and using Window handles to the WPF control.
To do this the .NET class HwndSource from the System.Windows.Interop namespace represents a WPF control in a Win32 window. This class derives from the base class PresentationSource that is an abstract class that can be used to map different technologies to WPF. HwndSource is the concrete class to offer Window handles.
The base class PresentationSource provides several static methods and properties to get and enumerate presentation sources. Presentation sources are added and removed by a derived class that invokes the base class members AddSource and RemoveSource.
HwndSource implements a Windows handle to wrap a Visual object that is a base class of WPF elements. Settings that can be assigned to the HwndSource are the position, height and width, the parent window, and window class styles.
The following code snippet shows the method GetUserControl1Hwnd that is implemented as a private member of the MFC dialog class. In this method a WPF control is instantiated, and this is assigned to the RootVisual property of the HwndSource object. The configuration of the new window is defined by setting the position, height, and width, the parent window, and the window class style. HwndSource can be configured by passing HwndSourceParameters to the constructor (as it is done in this code snippet), or passing separate values to a constructor overload. If the Window handle is needed, the HwndSource class defines the Handle property that returns an IntPtr. With this structure the ToPointer method returns a void*.
HWND CMFCDialogAppDlg::GetUserControl1Hwnd(HWND parent, int x, int y, int width, int height)
HwndSourceParameters^ sourceParams = gcnew HwndSourceParameters("MFCWPFApp");
sourceParams->PositionX = x;
sourceParams->PositionY = y;
sourceParams->Height = height;
sourceParams->Width = width;
sourceParams->ParentWindow = IntPtr(parent);
sourceParams->WindowStyle = WS_VISIBLE | WS_CHILD;
m_hwndSource = gcnew HwndSource(*sourceParams);
m_wpfUC = gcnew UserControl1();
m_hwndSource->RootVisual = m_wpfUC;
return (HWND) m_hwndSource->Handle.ToPointer();
For keeping a reference to a managed object in a unmanaged class (for example a reference to the WPF user control within the MFC dialog), it must be taken care of that the garbage collector does not release the managed object because no managed reference exists to it. Keeping the managed object can be done with the GCHandle structure that is a .NET value type from the namespace System.Runtime.InteropServices. GCHandle defines the static method Alloc that allocates and returns a GCHandle to a .NET objects. The instance method Free releases the reference to the object. The method ToIntPtr returns a native pointer that can be used by API methods.
Instead of using GCHandle directly, a gcroot object can be declared as a member of the class. gcroot is a native type defined within <vcclr.h> and wraps GCHandle for easy use. The constructor of this class allocates the GCHandle, and the destructor releases it.
gcroot here is used to define members to the WPF user control in the native dialog class.
Finally, the method GetUserControl1Hwnd where the WPF user control is instantiated is invoked on creation of the dialog. The first argument defines the parent window which is the window of the dialog itself. The method GetSafeHwnd of the base class CWnd returns the member m_hWnd, the window handle that is attached to the dialog or NULL.
int CMFCDialogAppDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
if (CDialogEx::OnCreate(lpCreateStruct) == -1)
m_hwndWPF = GetUserControl1Hwnd(this->GetSafeHwnd(), 20, 20, 200, 150);
Older blog entries about C++/CLI:
- Pointer Arithmetic with Embedded Value Types
- C++/CLI Value Types and Memory Location
- Dispose and Finalize with C++/CLI, C# and VB
- C++/CLI and IDisposable
- Instantiating Managed Objects
- Access Modifiers
- Properties with C++/CLI
More information on C++/CLI and interop between native and managed code in my Take off to C++/CLI workshop.