Learn to host WPF content as a control in MFC applications.
This article will showcase the technique to host WPF content in an MFC-based application. The main usage of this implementation is to enhance the graphical appearance of conventional Win32/MFC-based applications. The demo program that comes along with this article will host an animated WPF clock in an MFC dialog program.
Development Tools & Library
To build and run the demo program, the following components must be installed:
- MS Visual Studio 2005 or above
- MS .NET Framework 3.0 or above (download)
The .NET Framework 3.0 supported only in the following platforms:
- Windows Vista
- Windows XP SP2
- Windows Server 2003 SP1
If you are a C++ developer and want to use WPF as an add-on to enhance the graphical UI of your existing Win32/MFC applications, this article is written for you. To get the most out from this article, you should be familiar with VC++/CLi, MFC, XAML, and C#. But, if you don't know C# or XAML, you can still proceed just by referencing the generated DLL and make use of it in your MFC application.
What Is WPF?
WPF stands for Windows Presentation Foundation. It's a subsystem of Microsoft .NET Framework 3.0. It allows developers to productively create visually appealing applications and improve user experience. The deployment of WPF enables richer control, design, and development of the visual aspects of Windows programs. It aims to unify a host of application services: user interface, 2D and 3D drawing, fixed and adaptive documents, advanced typography, vector graphics, animation, data binding, audio, and video. WPF also provides a consistent programming model for building applications and provides a clear separation between the UI and the business logic.
WPF APIs are managed code, but most existing Win32/MFC programs are coded in unmanaged C++. Conventionally, WPF APIs cannot be called from a true unmanaged program. However, by using the /clr option with the VC++ compiler, you can create a mixed managed-unmanaged program where you can mix managed and unmanaged API calls seamlessly.
One thing to note is that you are not allowed to compile XAML files into a C++ project. So, you have the following options to handle this.
- Create a C# DLL that contains all your XAML pages as a compiled assembly, and then have your C++ executable include that DLL as a reference.
- Drop XAML and write all your WPF in code, building up the element tree from application.
This article will use the first approach.
There are two basic techniques for interoperation between WPF and Win32/MFC code:
- Host WPF in Win32/MFC: With this technique, developers can use the advanced graphics capabilities of WPF within the framework of standard Win32/MFC applications.
- Host Win32/MFC in WPF: With this technique, developers can use an existing custom Win32/MFC control in the context of other WPF content, and pass data across the boundaries.
This article will be based on the first technique.
Those are all the basic ground rules you should know; it'd time to get to the code. Here, I will show you WPF content created in XAML and C#, and the generated DLL will be referenced in VC++.
The idea is to create a simple Date & Time setting tool with the animated clock part implemented as a WPF content. The rest of the program still will be coded in MFC.
This demo program is constructed by two parts; one is MFCHostWPF (coded in VC++/MFC) and the other is WPFcontrols (coded in XAML and C#). The MFCHostWPF project will take the DLL generated by WPFcontrols project as an external reference.
Adding WPF as a Reference Library in an MFC Project
Adding WPF-Related Code to an MFC Application
For your information, the gcnew keyword is used to create an instance of managed type; this will create the instance on the garbage collected heap. All memory allocated by gcnew will be managed automatically by the garbage collector and developers will not need to worry about freeing them up.
To host WPF content, the key is on the System::Windows::Interop::HwndSource class. This class will take care of wrapping the WPF content in a Win32 window, so that the WPF content can be incorporated into your user interface (UI) as a child window. Communication with the WPF content object is done by using the reference stored in the static fields. They're declared as static to prevent them from being inadvertently garbage collected.
ref class Globals
static System::Windows::Interop::HwndSource^ gHwndSource;
static WPFControls::AnimClock^ gwcClock;
HWND hwndWPF; //The hwnd associated with the hosted WPF page
To create an HwndSource, first create an HwndSourceParameters structure and populate it with the following parameters:
- Class, window, and styles
- Initial position of the window
- Initial size of the window
- The parent window
Once you have populated the HwndSourceParameters structure, pass it to the HwndSource(HwndSourceParameters) constructor for the HwndSource.
Then, you create your WPF clock class by calling its constructor WPFControls::AnimClock(). You also initialize the date & time by calling its ChangeDateTime() method.
Finally, you assign the reference of the WPF clock object to the HwndSource object RootVisual property and return the HWND of the HwndSource by calling Handle.ToPointer().
HWND GetHwnd(HWND parent, int x, int y, int width, int height)
System::Windows::Interop::HwndSourceParameters^ sourceParams =
sourceParams->PositionX = x;
sourceParams->PositionY = y;
sourceParams->Height = height;
sourceParams->Width = width;
sourceParams->ParentWindow = IntPtr(parent);
sourceParams->WindowStyle = WS_VISIBLE | WS_CHILD;
DateTime tm = DateTime::Now;
Globals::gwcClock = gcnew WPFControls::AnimClock();
FrameworkElement^ myPage = Globals::gwcClock;
Globals::gHwndSource->RootVisual = myPage;
return (HWND) Globals::gHwndSource->Handle.ToPointer();
So, whenver the user makes date or time changes, your MFC code will call RefereshWPFControl() to refresh the WPF clock.
DateTime tm = DateTime::Now;
page = Globals::gwcClock;
Globals::gHwndSource->RootVisual = page;
Now that you have all your needed functions set, the last task is to find a place in your MFC dialog code to call the HwndSource instance creation function. There are a few possible locations to call it; one good place is in the OnCreate event handler.
Handle the WM_CREATE Notification
Call the GetHwnd() function in the OnCreate event handler when the application first starts.
int CMFCHostWPFDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
if (CDialog::OnCreate(lpCreateStruct) == -1)
hwndWPF = GetHwnd(this->GetSafeHwnd(), 20, 28, 205, 130);
As you can see now, by applying WPF in Win32/MFC applications you can add lots of "perks" to the overall user experience while the program logics remain coded in the Win32/MFC style.
All suggestions and corrections are welcome.