Category Archives: C++/CLI

Hosting a WPF control inside a MFC window and making the keyboard work

It’s quite possible to host a brand new shiny WPF control inside your smelly old MFC or even Win32 windows. It’s not particularly hard to do so, but you will need to do a few obscure things to make this work. Also, the intellisense that VS provides isn’t operational when working with C++/CLI. So you’ll pretty much have to know what you’re doing.

Similar code such as this can be found, but they often gloss over the details, such as how to deal with feeding the keyboard events to the child window, and how to make sure that the child window will receive tab events. I’ve included this as well, since you’ll probably want those things when integrating your new component.

First off, in your MFC project you’ll need to turn on support for common language runtime (the /clr flag) and you need to set the CLR-tread attribute to STA-thread in the project which ultimately hosts your .exe file. If you don’t do the STA-thread it will crash horribly and it will not be obvious why it has done so. In fact, the only hint is that it seems to crash even before it has a chance to be able to do so. That’s because the default setting is to handle the windows with multiple threads, something which WPF chokes on. The setting is found under Linker->Advanced.

For the sake of discussion lets say you have a dialog which extends the MFC class CDialog. You want to replace the contents of the window with a WPF control. First there is a few things that needs to be added, namely two references that needs to be kept in the dialog to keep the garbage collector from raining on our parade. In the header add two gcroot members:

gcroot m_hwndSource;
gcroot m_wpfControl;

Now to the more interesting bits, how to actually create an area for the WPF control to live in. What you actually need to do is to create something called a HwndSource. This object is a ‘real’ windows window and as such can receive windows messages and are actually understood by the windows system. A fundamental difference between WPF and Winforms/MFC/Win32 is that WPF does not have a HWND for every component that you create. It will only have one outer window and nothing more. This outer window is not created by itself, instead we need to provide such a window for WPF to live in. In some suitable place like the handler for WM_CREATE in your CDialog add this.

System::Windows::Interop::HwndSourceParameters^ sourceParams = gcnew System::Windows::Interop::HwndSourceParameters("MyWpfSourceWnd");
// This sets the position within the parent window
sourceParams->PositionX = 0;
sourceParams->PositionY = 0;
sourceParams->ParentWindow = System::IntPtr(this->GetSafeHwnd());
sourceParams->WindowStyle = WS_VISIBLE | WS_CHILD;

System::Windows::Interop::HwndSource^ source = gcnew System::Windows::Interop::HwndSource(*sourceParams);
MyWpfControl^ wpfControl = gcnew MyWpfControl();

// This creates sets the root visual of the interop component to out user control.
// As a sidenote, THIS is where it crashes if you have forgotten to set STA-thread.
source->RootVisual = wpfControl;

// Adds a keyboard hook!
source->AddHook(gcnew System::Windows::Interop::HwndSourceHook(ChildHwndSourceHook));

// This is the HWND of the interop component, should you choose to save it.
m_wpfChild = (HWND)source->Handle.ToPointer();
   
// This is important! If you do not save these the garbage collector eats them up!!
m_hwndSource = source;
m_wpfControl = wpfControl;

You might have noticed that we also added a hook to the window. This is needed, since if we do not do this your WPF control is never going to receive any keyboard events. The mouse however still works. This is a simple function outlined below:

System::IntPtr ChildHwndSourceHook(
    System::IntPtr /*hwnd*/, int msg,
    System::IntPtr /*wParam*/, System::IntPtr /*lParam*/, bool% handled)
{
    if (msg == WM_GETDLGCODE)
    {
        handled = true;
        // WANTCHARS = input boxes work
        // WANTTAB = tab order works
        // WANTALLKEYS = Enter key will work
        return System::IntPtr(DLGC_WANTCHARS | DLGC_WANTTAB |DLGC_WANTALLKEYS);
    }
    return System::IntPtr(0);
}

This is pretty much all you need to do. Note that there is nothing strange at ALL with the WPF usercontrol. No modifications needed, which also means that you can insert ANY of the cool WPF controls into your MFC application. Also, should you be stuck in Win32 country this works just as well, you just need to provide the parent HWND in another way than this->GetSafeHwnd().

Now go out and create your frankensteinian interop apps to terrorize the neighborhood.

Tagged , , ,

How to handle WPF events in native MFC code

I’ve recently had the dubious pleasure of getting to know C++/CLI and revisiting my good old buddy MFC. The task at hand was to integrate a WPF component into an existing MFC application. That task in itself is probably interesting enough to warrant it’s own post but suffice to say that I got this working properly. This means that we’ll end up with a class deriving CDialog that holds a WPF component inside it. Now we want to handle events. In WPF country, this is easy: add this to our WPF class:

public event EventHandler OnMyEvent;

And in our dialog we hold the WPF control in a gcroot<MyWPFControlClassName>. We also have a method we’d like to get called.

void MyDialogClass::OnEvent(System::Object ^sender, System::EventArgs ^args)
{
   // Do stuff
}

First off, what prompted this article is what does not work. You cannot from your unmanaged dialog class pass a pointer to your member function to your event like this.

wpfControl->OnMyEvent += gcnew System::EventHandler(
      this, 
      &MyNativeDialogClass::OnEvent);

This does not work, and it’s because your class is not a managed class since it’s not declared as ref class. The error message is also less than helpful. So how to solve this? You need a wrapper. Create this class somewhere in your project.

#pragma once

template<class T> ref class ManagedEventWrapper {
public:
   typedef void(T::*MemberFunction)(System::Object^ sender, System::EventArgs^ args);

   ManagedEventWrapper(T& host, MemberFunction function)
      : m_host (host)
      , m_function(function) 
   {
   }
   
   void OnEvent(System::Object ^sender, System::EventArgs ^args)
   {
      (m_host.*m_function)(sender, args);
   }

private:
   T& m_host;
   MemberFunction m_function;
};

If you then include this file in your MFC class, then you can subscribe to events like this:

wpfControl->OnMyEvent += gcnew System::EventHandler(
      gcnew ManagedEventWrapper<MyNativeDialogClass>(*this, &MyNativeDialogClass::OnEvent), 
      &ManagedEventWrapper<MyNativeDialogClass>::OnEvent);

This creates the neccessary layer of extra indirection around the native code to make the event handling work properly. You don’t need to save the reference to ManagedEventWraper since the reference is held by the System::EventHandler which makes sure it does not get garbage collected.

Tagged , , , ,