Position Based Mouse-Over in C#

Posted in software by Christopher R. Wirz on Thu Mar 28 2013



In WPF, event propagation is based on parent child relationships (nesting) and if that event has already been handled. When writing a control that is intended to be hosted by another application, overriding the OnMouse events or hooking into the event handles may have mixed results.

Note: The purpose of this example is not to use events at all

This is where older windows APIs come in handy once again. Specifically, this example uses the GetCursorPos method from user32.dll. In order to accomplish this, we need to define a Point struct that takes advantage of the sequential memory layout of the native API.


using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace Wpf
{
	/// <summary>
	///     Extension methods to enhance framework elements such as UI Elements
	/// </summary>
    public static partial class FrameworkElementExtensions
    {
        [StructLayout(LayoutKind.Sequential)]
        private struct Win32Point
        {
            public Int32 X;
            public Int32 Y;
        };

		[DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool GetCursorPos(ref Win32Point pt);

        /// <summary>
        ///     Determines whether the mouse is over an element.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <returns>
        ///     true if the mouse is over the element; otherwise, false.
        /// </returns>
        public static bool IsMouseOver(this UIElement element)
        {
            if (element == null){return false;}
            try
            {
                var position = element.PointToScreen(new Point(0d, 0d));
                var pt = new Win32Point();
                GetCursorPos(ref pt);
                if (pt.X < position.X || pt.Y < position.Y) {return false;}
                if (pt.X < position.X + element.RenderSize.Width &&
                    pt.Y < position.Y + element.RenderSize.Height)
                { return true; }
            }
            catch
            {
                // do nothing
            }
            return false;
        }
    }
}

The mouse needs to be within the rendered area of the UIElement. For a FrameworkElement, ActualWidth and ActualHeight work, but UIElement is a base class, so the extension method will work on both.

Now you can take any WPF element and call .IsMouseOver() to determine if the mouse is over that element/control.