Hosting WPF Content in an MFC Application

Introduction

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)

Platforms

The .NET Framework 3.0 supported only in the following platforms:

  • Windows Vista
  • Windows XP SP2
  • Windows Server 2003 SP1

Prerequisites

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.

Interoperation Basics

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.

Getting Started

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.

Project Division

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

Hosting WPF Content in an MFC Application

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
{
public:
   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 =
      gcnew System::Windows::Interop::HwndSourceParameters
      ("MFCWPFApp");
   sourceParams->PositionX    = x;
   sourceParams->PositionY    = y;
   sourceParams->Height       = height;
   sourceParams->Width        = width;
   sourceParams->ParentWindow = IntPtr(parent);
   sourceParams->WindowStyle  = WS_VISIBLE | WS_CHILD;
   Globals::gHwndSource =
      gcnew System::Windows::Interop::HwndSource(*sourceParams);

   DateTime tm = DateTime::Now;
   Globals::gwcClock = gcnew WPFControls::AnimClock();
   Globals::gwcClock->ChangeDateTime(tm.Year,tm.Month,tm.Day,
                                     tm.Hour,tm.Minute,tm.Second);
   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.

void RefreshWPFControl()
{
   FrameworkElement^ page;
   DateTime tm = DateTime::Now;

   Globals::gwcClock->ChangeDateTime(tm.Year,tm.Month,tm.Day,
                                     tm.Hour,tm.Minute,tm.Second);
   page = Globals::gwcClock;

   Globals::gHwndSource->RootVisual = page;

   return;
}

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)
      return -1;

   hwndWPF = GetHwnd(this->GetSafeHwnd(), 20, 28, 205, 130);

   return 0;
}

Conclusion

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.

Comments

All suggestions and corrections are welcome.



About the Author

Kevin Choong

Kevin is a C++ & C# developer with proven ability to architect, program, and optimize code. He has more than 10 years programming experience for business solutions. He also has the passion for both playing and developing video games. Kevin can be reached via kevin.choong@hotmail.com.

Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • On-demand Event Event Date: December 18, 2014 The Internet of Things (IoT) incorporates physical devices into business processes using predictive analytics. While it relies heavily on existing Internet technologies, it differs by including physical devices, specialized protocols, physical analytics, and a unique partner network. To capture the real business value of IoT, the industry must move beyond customized projects to general patterns and platforms. Check out this webcast and join industry experts as …

  • Today's agile organizations pose operations teams with a tremendous challenge: to deploy new releases to production immediately after development and testing is completed. To ensure that applications are deployed successfully, an automatic and transparent process is required. We refer to this process as Zero Touch Deployment™. This white paper reviews two approaches to Zero Touch Deployment--a script-based solution and a release automation platform. The article discusses how each can solve the key …

Most Popular Programming Stories

More for Developers

RSS Feeds