Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mouse handlers in WPF/Caliburn Micro

I've got what I consider a simple situation, but can't seem to figure out how to get it done. I'm very new to WPF and the Caliburn Micro framework. In my View, I have a Canvas and on this Canvas I have an Image. I want to write some mouse event handlers (MouseLeftButtonDown, MouseLeftButtonUp, and MouseMove) that are called when the cursor is inside the Image. I have successfully been able to create event handlers in the ViewModel, but I can't seem to figure out how to get the current cursor position inside the handlers. The MouseEventArgs.GetPosition requires a IInputElement as an argument...not sure how to get that.

Here's my XAML (view):

<Canvas x:Name="ImageCanvas" >    
    <Image Source="{Binding DataImage}" 
           x:Name="ContainerImage"
              cal:Message.Attach=
              "[Event MouseLeftButtonDown] = [Action MouseDown_Image($source, $eventArgs)];
               [Event MouseLeftButtonUp] = [Action MouseUp_Image($source, $eventArgs)];
               [Event MouseMove] = [Action MouseMove_Image($source, $eventArgs)]">
    </Image>
</Canvas>

Here's my C# (viewmodel)

public void MouseDown_Image(object sender, MouseEventArgs e)
    {
      // How do I get cursor position here??

      // convert to Image coordinates??
    }

public void MouseUp_Image(object sender, MouseEventArgs e)
    {
      // How do I get cursor position here??

      // convert to Image coordinates??
    }

public void MouseMove_Image(object sender, MouseEventArgs e)
    {
      // How do I get cursor position here??

      // convert to Image coordinates??
    }

Once I do this, I'll need to convert the mouse coordinates to coordinates on the Image...but first things first.

Thanks!!

like image 703
Bryan Greenway Avatar asked Jul 01 '13 22:07

Bryan Greenway


1 Answers

It may be useful to make a SpecialValues dictionary entry in order to process the coords for an image - this way you could add functionality in a central location, or even take just the X/Y position within the image or another scaled input control:

e.g. in your CM Bootstrapper configure:

            MessageBinder.SpecialValues.Add("$scaledmousex", (ctx) =>
            {
                var img = ctx.Source as Image;
                var input = ctx.Source as IInputElement;
                var e = ctx.EventArgs as MouseEventArgs;

                // If there is an image control, get the scaled position
                if (img != null && e != null)
                {
                    Point position = e.GetPosition(img);
                    return (int)(img.Source.Width * (position.X / img.ActualWidth));
                }

                // If there is another type of of IInputControl get the non-scaled position - or do some processing to get a scaled position, whatever needs to happen
                if (e != null && input != null)
                    return e.GetPosition(input).X;

                // Return 0 if no processing could be done
                return 0;
            });

This dictionary is used to parse the input parameters in your action message binding and will run the above anonymous delegates, returning the value you specify (you will need to also add $scaledmousey)

Then you use this value in your binding:

cal:Message.Attach="[Event MouseMove] = [Action MouseMove_Image($scaledmousex, $scaledmousey)]"

This way, if you decide you want to change the source type, you can still get the position without much/any refactoring and your code goes through a central processing path

This also ensures that the view/viewmodel are decoupled - part of the aim of the MVVM pattern

like image 89
Charleh Avatar answered Sep 22 '22 01:09

Charleh