wpfwinapimfcc++-cli

Non-Modal WPF control hosted in MFC Dialog does not receive keyboard input


To modernize an older C++/CLI MFC-based application we are trying to replace an old dialog with a new one written in C#/WPF. To host the WPF control inside a CDialog we have followed this guide describing the procedure for a Win32 host window: Hosting WPF Content in Win32

This works but the textboxes inside the WPF control do not accept/receive any keyboard input despite special keys like [backspace]/[del]/[space].

There is a similar question Non-modal WPF dialog hosted in a Win32 app doesn't receive keyboard events where the only answer advises to use the HwndSource to implement a message loop within the WPF control's assembly to receive/process the keyboard related messages but its not working. I implemented it in this way but it doesn't change anything.

private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
    HwndSource source = (HwndSource)PresentationSource.FromVisual(this);
    source.AddHook(WndProc);
}

private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    HwndSource source = HwndSource.FromHwnd(hwnd);
    if (source != null)
    {
        System.Windows.Interop.MSG message = new MSG
        {
            hwnd = hwnd,
            lParam = lParam,
            wParam = wParam,
            message = msg,
            pt_x = 0,
            pt_y = 0
        };

        switch (msg)
        {
            // WM_KEYDOWN
            case 0x0100:
                Debug.WriteLine("Received WM_KEYDOWN");
                var keyboardSink = (IKeyboardInputSink)source;
                keyboardSink.TranslateAccelerator(ref message, Keyboard.Modifiers);
            break;

            // WM_CHAR
            case 0x0102:
                Debug.WriteLine("Received WM_CHAR");
                var keyboardSink2 = (IKeyboardInputSink)source;
                keyboardSink2.TranslateChar(ref message, Keyboard.Modifiers);
            break;
        }
    }

    return IntPtr.Zero;
}

However embedding the same WPF control inside a bare win32 window works flawlessly.

Also textboxes inside a modal-dialog which I spawn from the main WPF control receive keyboard inputs as well, so it must be related to the non-modal fashion of the main control.

Here I put a minimal working example to demonstrate the issue: https://github.com/Michaelvsk/WpfHostedInMfc Screenshot of example app

My MFC knowledge is very basic, so any help is much appreciated.


Solution

  • In your case, the WPF control is hosted as a WS_CHILD (this is important) of a Win32 dialog box (this is also important), which eats some messages for its own good, as all dialog boxes do.

    You can control that if you handle the WM_GETDLGCODE message

    Sent to the window procedure associated with a control. By default, the system handles all keyboard input to the control; the system interprets certain types of keyboard input as dialog box navigation keys. To override this default behavior, the control can respond to the WM_GETDLGCODE message to indicate the types of input it wants to process itself.

    Something like this:

    private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        const int WM_GETDLGCODE = 0x0087;
        const int DLGC_WANTALLKEYS = 4;
    
        if (msg == WM_GETDLGCODE)
        {
            handled = true;
            return new IntPtr(DLGC_WANTALLKEYS);
        }
        return IntPtr.Zero;
    }