Bubble Mouse Move events from child controls up the hierarchy in Windows Forms

Windows Forms (or „WinForms“ for short) does not know the concept of event bubbling (also called „event propagation“ sometimes). To solve this in terms of command routing, I’ve written some small classes earlier.

To bubble up events from child controls to parent controls (or the form itself), the idea is to hook into the child control creation and hook up for those specific events and manually forward them.

Based on this idea and with the help of a forum answer on MSDN, I’ve written a small class that you can attach to a control and get all child control MouseMove events. The class looks like:

public sealed class MouseEventBubbler
{
    private readonly Control _attachTo;

    public MouseEventBubbler(Control attachTo)
    {
        _attachTo = attachTo;

        _attachTo.MouseMove += _attachTo_MouseMove;

        _attachTo.ControlAdded += _attachTo_ControlAdded;
        _attachTo.ControlRemoved += _attachTo_ControlRemoved;

        foreach (Control control in _attachTo.Controls)
        {
            AttachToControl(control);
        }
    }

    public void _attachTo_MouseMove(object sender, MouseEventArgs e)
    {
        OnMouseMove(e);
    }

    public event MouseEventHandler MouseMove;

    private void _attachTo_ControlAdded(object sender, ControlEventArgs e)
    {
        AttachToControl(e.Control);
    }

    private void _attachTo_ControlRemoved(object sender, ControlEventArgs e)
    {
        DetachFromControl(e.Control);
    }

    private void AttachToControl(Control c)
    {
        c.MouseMove += Child_MouseMove;
        c.ControlAdded += Child_ControlAdded;
        c.ControlRemoved += Child_ControlRemoved;
        AttachToChildren(c);
    }

    private void AttachToChildren(Control parent)
    {
        foreach (Control child in parent.Controls)
        {
            AttachToControl(child);
        }
    }

    private void DetachFromControl(Control c)
    {
        DetachFromChildren(c);
        c.MouseMove -= Child_MouseMove;
        c.ControlAdded -= Child_ControlAdded;
        c.ControlRemoved -= Child_ControlRemoved;
    }

    private void DetachFromChildren(Control parent)
    {
        foreach (Control child in parent.Controls)
        {
            DetachFromControl(child);
        }
    }

    private void Child_ControlAdded(object sender, ControlEventArgs e)
    {
        AttachToControl(e.Control);
    }

    private void Child_ControlRemoved(object sender, ControlEventArgs e)
    {
        DetachFromControl(e.Control);
    }

    private void Child_MouseMove(object sender, MouseEventArgs e)
    {
        var pt = e.Location;
        var child = (Control)sender;
        do
        {
            pt.Offset(child.Left, child.Top);
            child = child.Parent;
        }
        while (child != _attachTo);

        var newArgs = new MouseEventArgs(e.Button, e.Clicks, pt.X, pt.Y, e.Delta);
        OnMouseMove(newArgs);
    }

    private void OnMouseMove(MouseEventArgs newArgs)
    {
        var h = MouseMove;
        if (h != null)
        {
            h(this, newArgs);
        }
    }
}

I’ve also saved it as a PasteBin.

The class can be adjusted to match other events than the MouseMove event, if required.

How to solve „An app on your PC needs the following Windows feature: .NET Framework 3.5 (includes .NET 2.0 and 3.0)“ on Windows 8

Are you a software developer and ever got this message when testing your .NET 2.0 WinForms application on Windows 8?

An app on your PC needs the following Windows feature: .NET Framework 3.5 (includes .NET 2.0 and 3.0)

An app on your PC needs the following Windows feature: .NET Framework 3.5 (includes .NET 2.0 and 3.0)

Even though your application would run on .NET 4, too?

Here is my story on why this happended to me and how to solve.

Why this happens

When you run a .NET application, Windows 8 seems to check whether the required .NET Framework is available. If it is not available, the above message is being displayed.

How to solve it

Windows 8 cannot know whether your application, compiled for .NET 2, also runs on .NET 4. It has the following knowledge:

  • The .NET Framework you compiled your application for
  • The processor bitness you compiled your application for (i.e. „x86“ or „Any CPU“)

If you want Windows 8 to be aware of anything other, you have to tell. To change the .NET Framework version, I created a configuration file with the same name as the application, in the same folder.

E.g. if your application is „my-app.exe“, your configuration file has to be „my-app.exe.config“. I use the following content of the file:

<?xml version="1.0"?>
<configuration>
    <startup useLegacyV2RuntimeActivationPolicy="true">
        <supportedRuntime version="v2.0.50727"/>
        <supportedRuntime version="v4.0"/>
    </startup>
</configuration>

I.e. you have to tell the supported runtime.

This is sometimes not enough, though. In my scenario, I had the application compiled as „Any CPU“ and configured for V2.0.50727 and v4.0 but still the above Windows message appeared.

My mistake was that on the test system, I only had a 32 bit V2.0.50727, not 64-bit, although the Windows itself was 64-bit. (I don’t know whether a 64-bit version of the .NET Framework exists at all)

So to solve this, I re-compiled the application to „x86“ and then, everything worked perfectly.

(Please correct me if any of my technical assumptions above are wrong)

Handling WM_MOVING in Windows Forms

Just a quick snippet:

public class FormWithWmMoving :
    Form
{
    private const int WM_MOVING = 0x0216;

    private static readonly object EVENT_MOVING = new object();

    public event EventHandler Moving
    {
        add { Events.AddHandler(EVENT_MOVING, value); }
        remove { Events.RemoveHandler(EVENT_MOVING, value); }
    }

    public class MovingEventArgs : EventArgs
    {
        private readonly Rectangle _rectangle;

        public MovingEventArgs(
            Rectangle rectangle)
        {
            _rectangle = rectangle;
        }

        public Rectangle Rectangle
        {
            get { return _rectangle; }
        }
    }

    protected virtual void OnMoving(MovingEventArgs e)
    {
        var h = (EventHandler)Events[EVENT_MOVING];
        if (h != null)
        {
            h(this, e);
        }
    }

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == WM_MOVING)
        {
            var r = (Win32NativeMethods.RECT)Marshal.PtrToStructure(m.LParam, typeof(Win32NativeMethods.RECT));
            var rectangle = new Rectangle(r.left, r.top, r.Bounds.Width, r.Bounds.Height);

            var args = new MovingEventArgs(rectangle);
            OnMoving(args);
        }

        base.WndProc(ref m);
    }
}

Hope this is helpful someday to me or others.

Fehlermeldung „Klasse unterstützt keine Automatisierung“ beheben

In einem .NET-Programm (Zeta Producer, logisch) in dem ich Microsoft Active Scripting verwendet habe, kam beim Verwenden einer neuen Klasse (Zugriff aus einem VBScript auf diese Klasse) die Fehlermeldung:

Klasse unterstützt keine Automatisierung

Bzw. in Englisch:

Class does not support Automation

Die Lösung war dann ganz einfach, ich hatte die Klasse als „internal“ markiert, anstatt als „public„.

Falsch:

[ComVisible(true)]
internal sealed class MyClass
{
}

Richtig:

[ComVisible(true)]
public sealed class MyClass
{
}

So einfach ist das, nach nur 4 Stunden habe ich es schon erkannt.