3
votes

I have Winforms control within a WPF WindowsFormsHost. The Winforms control is passive and must not handle any mouse event. Mouse events should be raised as usual from the most inner WPF control in the WPF visual tree, so the WindowsFormsHost (or the next one). But no event is triggered at all.

How should I configure the WindowsFormsHost, the Winforms control, in order to achieve this?

Remark: KeyDown and KeyUp behave as expected. But Mouse Events don't, as illustrated by the following Snoop screenshot:

enter image description here

1
Winforms does little to alter the way Windows generates and routes notifications beyond keyboard shortcut handling and reflecting certain messages from the parent back to the child. MouseWheel bubbles to the parent, the rest do not. WindowsFormsHost doesn't do anything to change this. So this is entirely expected. If a control is not supposed to receive input events then its Enabled property should be set to false. You probably don't like that either :)Hans Passant
Thanks! Please explain... if you replace the WindowsFormsHost by a simple WPF UserControl, then the bubbling works and the MouseDown event bubbles up and raises the subscriber's event-hanlders on the way up. It seems that the Winforms Control keeps the mouse event for himself and doesn't forward the event to its host. Is that correct?jeromerg

1 Answers

7
votes

Indeed the Winforms Control keeps the mouse event for himself and doesn't forward the event to its host. The solution is to subscribe to the winforms MouseDown event and generate programmatically the Routed Event.

I overrided the WindowsFormsHost as following and it rocks:

(remark: a behavior may be more flexible)

public class ExtendedWindowsFormsHost : WindowsFormsHost
{
    public ExtendedWindowsFormsHost()
    {
        ChildChanged += OnChildChanged;
    }

    private void OnChildChanged(object sender, ChildChangedEventArgs childChangedEventArgs)
    {
        var previousChild = childChangedEventArgs.PreviousChild as Control;            
        if (previousChild != null)
        {
            previousChild.MouseDown -= OnMouseDown;
        }
        if (Child != null)
        {
            Child.MouseDown += OnMouseDown;
        }
    }

    private void OnMouseDown(object sender, MouseEventArgs mouseEventArgs)
    {
        MouseButton? wpfButton = ConvertToWpf(mouseEventArgs.Button);
        if (!wpfButton.HasValue)
            return;

        RaiseEvent(new MouseButtonEventArgs(Mouse.PrimaryDevice, 0, wpfButton.Value)
        {
            RoutedEvent = Mouse.MouseDownEvent,
            Source = this,
        });
    }

    private MouseButton? ConvertToWpf(MouseButtons winformButton)
    {
        switch (winformButton)
        {
            case MouseButtons.Left:
                return MouseButton.Left;
            case MouseButtons.None:
                return null;
            case MouseButtons.Right:
                return MouseButton.Right;
            case MouseButtons.Middle:
                return MouseButton.Middle;
            case MouseButtons.XButton1:
                return MouseButton.XButton1;
            case MouseButtons.XButton2:
                return MouseButton.XButton2;
            default:
                throw new ArgumentOutOfRangeException("winformButton");
        }
    }
}