Monday, December 22, 2008

Getting The Current Mouse Position The Windows Presentation Foundation

While working on a Windows Presentation Foundation post now and I ran across an oddity between Windows Forms and WPF. The System.Windows.Input.MouseEventArgs object does not contain the current position of the mouse.

In the Windows Forms world, the event arguments contained the cursor position. You could do something to the effect of:
protected void image1_Click(object sender, MouseEventArgs e)
{
    int x = e.X;
    int y = e.Y;
    // Do something with the coordinates

}

Leaving the Win32/Windows Forms world behind, things change in WPF. In WPF, the MouseEventArgs actually points to the System.Windows.Input.MouseEventArgs object and not the System.Windows.Forms.MousEventArgs object.
protected void image1_Click(object sender, MouseEventArgs e)
{
    FrameworkElement control = (FrameworkElement)sender;
    Point MousePos = Mouse.GetPosition(control);
    
}
Or we can use the event like this:
protected void image1_Click(object sender, MouseEventArgs e)
{
    Point position = e.GetPosition(this);
}
So, this is great, we can find the position of the mouse again.

Taking this a bit farther, what if we were upgrading a windows forms application to WPF and wanted to minimize the code we would have to change?

HINT: there is no drop in replacement for it.

I will go through some of the mental gymnastics and share some possible solutions with the implications.

It might be nice to extend the MouseEventArgs. This currently is not possible because the concept of "extension properties" is not implemented. Perhaps in .Net 4.0. We could implement something similar with extension methods requiring the use of parenthesizes, which would not be a drop-in replacement. That leaves us with 2 options, 1) Use partial class to add the properties and 2) play the namespace game and create our own class that inherits the System.Windows.Input.MouseEventArgs.

We can rule out the partial class because the System.Windows.Input.MouseEventArgs object is not a partial class. So the other options have failed to bare fruit, it appears that we would have to fall back to the old faithful object oriented convention of programming, inheritance. We could produce the following:
namespace System_Windows_Input2
{
    public class MouseEventArgs : System.Windows.Input.MouseEventArgs
    {
        private Point currentPosition;
        private bool isLoaded;
        private Point CurrentPosition {
            get{
                if (!isLoaded) {
                    isLoaded = true;
                    FrameworkElement control = (FrameworkElement)Source;
                    currentPosition = Mouse.GetPosition(control);
                }
                return currentPosition;
            }
        }
        public double X
        {
            get {
                return CurrentPosition.X;
            }
        }
        public double Y
        {
            get
            {
                return CurrentPosition.Y;
            }
        }
    }
}
As long as the namespaces are listed in the correct order, System_Windows_Input2.MouseEventArgs will shadow the System.Windows.Input.MouseEventArgs. I.e.:
using System.Windows.Input;
using System_Windows_Input2;
Success! Right? We must ask ourselves, "is this way worth it?" and "are there unexpected consequences?". Myself, I would have to be really hard up and in a hole to seriously consider this as a possibiility. This really just buys us a small amount of updating to the mouse event handlers and in all probability the application is not going to have an overly significant amount of them. Worse, it breaks the new standard way of getting the current mouse position; meaning that maintainability goes down.

If we use System.Windows.Input.MouseButtonEventArgs, System.Windows.Input.MouseWheelEventArgs, System.Windows.Input.QueryCursorEventArgs objects, they will not contain the X and Y properties because they inherit from the System.Windows.Input.MouseEventArgs, not our modified class; we'll call this an unexpected consequence. To achieve the the same effect as our System_Windows_Input2.MouseEventArgs class, we would have to implement the same game with the other classes. Leading to more code, more bloat, and less maintainability.

So, we can implement the same functionality we had in the Windows Forms world, but in the end gains don't out weigh the downsides. It will likely take more code to re-implement the old way than to just update the code to the new standard way of doing things. Happy coding!

No comments: