dotnet / wpf

WPF is a .NET Core UI framework for building Windows desktop applications.
MIT License
7.1k stars 1.17k forks source link

Native QWidget in HwndHost works, but no override methods are called except BuildWindowCore, why? #7240

Open emmenlau opened 2 years ago

emmenlau commented 2 years ago

I have a native window created in Qt with Qt Widgets. I embed the window in WPF with the help of a class derived from HwndHost. The embedding works and I can see the widget, and basic mouse interaction also works. However I would want to receive further events, for example keyboard events. Also I would like the DestroyWindowCore method to be called. But I can get neither of this to work. In my understanding, a good start for keyboard support would be to get WndProc or OnGotKeyboardFocus working, but I fail to see how to proceed...

Any help would be greatly appreciated!

I have the following override method that works:

        protected override HandleRef BuildWindowCore(HandleRef aHWNDParent)

And I have the following overrides that never get called, not on key presses, not on mouse interaction, not on closing the application, never (checked with logging, and with the coreclr debugger):

        protected override void DestroyWindowCore(HandleRef aHWND)
        protected override IntPtr WndProc(IntPtr aHWND, int aMessage, IntPtr aWParam, IntPtr aLParam, ref bool aHandled)
        protected override bool TabIntoCore(TraversalRequest request)
        protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
        protected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
        protected override bool TranslateAcceleratorCore(ref MSG msg, ModifierKeys modifiers)

Actual behavior:

The override methods are not called.

Expected behavior:

The override methods are called.

Minimal repro:

This is my WPF HwndHost container window. In this I place a QWidget that is rendered from a separate thread, the Qt UI thread. The widget works and behaves correctly but never gets keyboard focus, and none of my override methods are called.

using System;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Threading;
using System.Windows.Input;

namespace Managed.WPF
{
    public class TestNativeWidget : HwndHost
    {
        private static Serilog.ILogger mLogger => Serilog.Log.ForContext<TestNativeWidget>();

        protected IntPtr mTestNativeWidgetHandle = IntPtr.Zero;

        public TestNativeWidget()
        {
        }

        protected override HandleRef BuildWindowCore(HandleRef aHWNDParent)
        {
            var reParentTask = Task.Run(() =>
            {
                NativeBindingHolder.Get().reParentWidgetSlot(NativeWindowType.TestNativeWidget, aHWNDParent.Handle);
                mTestNativeWidgetHandle = NativeBindingHolder.Get().getWidgetHandleSlot(NativeWindowType.TestNativeWidget);
            });

            while (!reParentTask.IsCompleted && !reParentTask.IsFaulted)
            {
                Dispatcher.CurrentDispatcher.Invoke(new Action(delegate { }), DispatcherPriority.Background);
            }

            if (reParentTask.IsFaulted) throw reParentTask.Exception;

            return new HandleRef(this, mTestNativeWidgetHandle);
        }

        protected override void DestroyWindowCore(HandleRef aHWND)
        {
            NativeBindingHolder.Get().destroyNativeEmbeddedWidgetSlot(NativeWindowType.TestNativeWidget);
        }

        protected override IntPtr WndProc(IntPtr aHWND, int aMessage, IntPtr aWParam, IntPtr aLParam, ref bool aHandled)
        {
            return base.WndProc(aHWND, aMessage, aWParam, aLParam, ref aHandled);
        }

        protected override bool TabIntoCore(TraversalRequest request)
        {
            OperatingSystemMethodsBindings.SetFocus(mTestNativeWidgetHandle);
            return true;
        }

        protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
        {
            if (!e.Handled)
            {
                OperatingSystemMethodsBindings.SetFocus(mTestNativeWidgetHandle);
            }

            base.OnGotKeyboardFocus(e);
        }

        protected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
        {
            base.OnLostKeyboardFocus(e);
        }
    }
}
lindexi commented 2 years ago

@emmenlau I recommend that you also create a copy of this issues in QT

And sorry I can't help you because I don't know qt.

emmenlau commented 2 years ago

Dear @lindexi , thanks for the quick response!

But I do not think the problem is specific to Qt. Or how can it be? The overloaded methods should be called from WPF, not from Qt, or am I on the wrong track?

emmenlau commented 1 year ago

Anyone?

lindexi commented 1 year ago

@emmenlau Probably very few friends can answer that question.

emmenlau commented 1 year ago

Oh my, that is very unfortunate! I'm under the impression that the embedding of native widgets does not fully work, but there seems to be nobody that can help. Is there some way this could be escalated to the relevant people?

miloush commented 1 year ago

@emmenlau including a project people can run and reproduce the issue with might help

pchaurasia14 commented 1 year ago

@emmenlau - Please provide a minimal repro for us to check this further

emmenlau commented 1 year ago

Ok I'll try to make a public repo with a repro. Can you install Qt yourself somehow, for example through the OS, or vcpkg or as a download from the Qt website?