WPF/Caliburn Micro中的鼠标处理程序

7
我认为这是一个简单的情况,但似乎无法想出如何完成它。我对WPF和Caliburn Micro框架非常陌生。在我的View中,我有一个Canvas,在这个Canvas上我有一个Image。 我想编写一些鼠标事件处理程序(MouseLeftButtonDown,MouseLeftButtonUp和MouseMove),当光标位于Image内时调用这些处理程序。 我已经成功地能够在ViewModel中创建事件处理程序,但我似乎无法弄清楚如何在处理程序中获取当前光标位置。MouseEventArgs.GetPosition需要一个IInputElement作为参数……不确定如何获得它。
以下是我的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>

这是我的C#代码(视图模型)

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??
    }

我完成这个任务后,需要将鼠标坐标转换为图像上的坐标...但首先要做好第一步。

谢谢!


这不是MVVM的目的。你永远不应该在ViewModel中创建事件处理程序。如果你想要处理鼠标事件,应该在视图中处理。如果你想将鼠标事件转换为特定的操作,请创建一个转换器来将事件转换为该操作。 - Panagiotis Kanavos
2个回答

6

为了处理图像的坐标,创建一个SpecialValues字典条目可能是有用的 - 这样你可以在一个中心位置添加功能,甚至只能获取图像内或另一个缩放输入控件中的X/Y位置:

例如,在您的CM Bootstrapper中进行配置:

            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;
            });

该字典用于解析您操作消息绑定中的输入参数,并运行上述匿名委托,返回您指定的值(您还需要添加$scaledmousey)。

然后您可以在绑定中使用此值:

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

这样,如果您决定更改源类型,仍然可以轻松获取位置,而无需进行任何重构,并且您的代码将通过一个中央处理路径。

这也确保了视图/视图模型的解耦-这是MVVM模式的目标之一。


Charleh - 感谢您的回复。从我收到的其他回复中,我看到我通过在Action中传递eventArgs破坏了MVVM。您的建议似乎解决了这个问题。我很快意识到我需要回头学习更多关于WPF和MVVM的知识。再次感谢您友善而详细的回复。 - Bryan Greenway
有时候似乎从 VM 引用已知的类型视图是唯一的选择,但大多数情况下都有一种机制可以避免这种情况,而且通常在 CM 中内置了此功能。我总是问自己“这段代码如何确保我们不会将这些组件耦合在一起?”,通常通过讨论能帮助我找到一种替代方案——常常是更干净、更简单、具有更好分离性的实现!祝你好运! - Charleh
我有一个问题,不过 :-) ,你提供的代码是否放在CM引导程序类中?当我这样做时,我会收到一个错误:“Caliburn.Micro.MessageBinder.SpecialValues是一个字段,但被用作类型”。我错过了什么? - Bryan Greenway
应该放在您的 Bootstrapper.Configure 方法内 - SpecialValuesMessageBinder 类上的静态字段(如果我没记错,它是一个 Dictionary<string, Func>),因此代码只需向字典添加一个键和回调函数。 - Charleh

0

我相信我已经解决了它!

在我的XAML中,我将事件处理程序附加到图像对象上的事件。因此,在我的事件处理程序内部,我的发送器可以转换为Image类型。然后,您可以将发送器作为参数传递给GetPosition。

此外,我能够将发送的坐标转换为实际图像坐标。知道原始图像的像素大小,只需要进行一些简单的缩放数学运算即可。

这是我的一个鼠标处理程序的代码:

public void MouseDown_Image(object sender, MouseEventArgs e)
   {

      // Get the x and y coordinates of the mouse pointer.
       System.Windows.Point position = e.GetPosition((Image)sender);
       int pX = (int)position.X;
       int pY = (int)position.Y;

     // DataImage is the actual Image object that is being shown in the view
       int X_ImageCoordinates = (int)(DataImage.PixelWidth * (position.X/img.ActualWidth));
       int Y_ImageCoordinates = (int)(DataImage.PixelHeight * (position.Y / img.ActualHeight));                
    }

原来很简单!就应该是这样。

如果有更好的解决方案或者我犯了错误,请告诉我。

谢谢!


你刚刚使MVVM模型失效了。MVVM模型的整个重点在于将视图与模型分离。通过将事件传递到模型,您破坏了这一点。如果您确实需要处理鼠标事件,请在视图中处理。 - Panagiotis Kanavos
明白了。谢谢您。看起来我还有很多要学习关于MVVM,CM和WPF的知识。 - Bryan Greenway

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接