c# - 使用 MVVM 相对于 WPF 中图像的鼠标位置

标签 c# wpf xaml mvvm

当鼠标位于 wpf 应用程序中的图像上方时,我试图符合 MVVM 结构。鼠标位置应转换为相对于图像的像素位置。

当 Image_MouseMove 在 ImagePositionView.xaml.cs 中时,我有这个工作,但对于如何使用 MVVM 结构来实现这一点,我有点茫然(即使在尝试读取其他线程之后)。

我添加了对 MVVMLight 的引用,希望这将使这项任务更容易,但我以前从未习惯过......

这是我到目前为止所拥有的:

看法:

我根据我所看到的添加了这些引用:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"xmlns:cmd="http://www.galasoft.ch/mvvmlight"

<UserControl x:Class="ImagePixelLocation.View.ImagePositionView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    xmlns:cmd="http://www.galasoft.ch/mvvmlight"
    xmlns:local="clr-namespace:ImagePixelLocation"
    mc:Ignorable="d" 
    d:DesignHeight="600" d:DesignWidth="1000" Background="White">

   <Grid>
       <Viewbox HorizontalAlignment="Center">
           <Grid Name="ColorImage">
               <Image x:Name="ImageOnDisplay" Source="{Binding ColourImage}" Stretch="UniformToFill" />
           </Grid>
       </Viewbox>
   </Grid>

</UserControl>

View 模型:

ViewModelBase 公开 INofityPropertyChanged 和 IDisposable
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using GalaSoft.MvvmLight;

namespace ImagePixelView.ViewModel
{
    class ImagePositionViewModel : ViewModelBase
    {

        private WriteableBitmap colourBitmap = null;

        public ImageSource ColourImage
        {
            get
            {
                return this.colourBitmap;
            }
        }


        public ManualSelectionViewModel()
        {
            // Open image to writeablebitmap
            string path = @"C:\Some\Path\To\ColorImage.png";

            Stream imageStreamSource = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
            var decoder = new PngBitmapDecoder(imageStreamSource, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
            BitmapSource source = decoder.Frames[0];

            int width = source.PixelWidth;
            int height = source.PixelHeight;
            int stride = source.Format.BitsPerPixel / 8 * width;
            byte[] data = new byte[stride * height];
            source.CopyPixels(data, stride, 0);

            this.colourBitmap = new WriteableBitmap(width, height, 96.0, 96.0, source.Format, null);
            this.colourBitmap.WritePixels(new Int32Rect(0, 0, width, height), data, stride, 0);
        }


        private void Image_MouseMove(object sender, MouseEventArgs e)
        {
            BitmapSource bitmapImage = (BitmapSource)this.ColourImage;

            string xCoord = (e.GetPosition(ImageOnDisplay).X * bitmapImage.PixelWidth / ImageOnDisplay.ActualWidth).ToString();
            string yCoord = (e.GetPosition(ImageOnDisplay).Y * bitmapImage.PixelHeight / ImageOnDisplay.ActualHeight).ToString();

            System.Diagnostics.Debug.WriteLine("mouse location is X:" + xCoord + ", Y:" + yCoord);
        }

    }
}

我想主要是如何访问 View 元素 ImageOnDisplay来自ImagePositionViewModel .

最佳答案

我用一种行为来做到这一点。首先我声明一个我的 View 模型将实现的接口(interface):

public interface IMouseCaptureProxy
{
    event EventHandler Capture;
    event EventHandler Release;

    void OnMouseDown(object sender, MouseCaptureArgs e);
    void OnMouseMove(object sender, MouseCaptureArgs e);
    void OnMouseUp(object sender, MouseCaptureArgs e);
}

public class MouseCaptureArgs
{
    public double X {get; set;}
    public double Y { get; set; }
    public bool LeftButton { get; set; }
    public bool RightButton { get; set; }
}

这是一个使用它的行为:
public class MouseCaptureBehavior : Behavior<FrameworkElement>
{
    public static readonly DependencyProperty ProxyProperty = DependencyProperty.RegisterAttached(
        "Proxy",
        typeof(IMouseCaptureProxy),
        typeof(MouseCaptureBehavior),
        new PropertyMetadata(null, OnProxyChanged));

    public static void SetProxy(DependencyObject source, IMouseCaptureProxy value)
    {
        source.SetValue(ProxyProperty, value);
    }

    public static IMouseCaptureProxy GetProxy(DependencyObject source)
    {
        return (IMouseCaptureProxy)source.GetValue(ProxyProperty);
    }

    private static void OnProxyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (e.OldValue is IMouseCaptureProxy)
        {
            (e.OldValue as IMouseCaptureProxy).Capture -= OnCapture;
            (e.OldValue as IMouseCaptureProxy).Release -= OnRelease;
        }
        if (e.NewValue is IMouseCaptureProxy)
        {
            (e.NewValue as IMouseCaptureProxy).Capture += OnCapture;
            (e.NewValue as IMouseCaptureProxy).Release += OnRelease;
        }
    }

    static void OnCapture(object sender, EventArgs e)
    {
        var behavior = sender as MouseCaptureBehavior;
        if (behavior != null)
            behavior.AssociatedObject.CaptureMouse();
    }

    static void OnRelease(object sender, EventArgs e)
    {
        var behavior = sender as MouseCaptureBehavior;
        if (behavior != null)
            behavior.AssociatedObject.ReleaseMouseCapture();
    }

    protected override void OnAttached()
    {
        base.OnAttached();
        this.AssociatedObject.PreviewMouseDown += OnMouseDown;
        this.AssociatedObject.PreviewMouseMove += OnMouseMove;
        this.AssociatedObject.PreviewMouseUp += OnMouseUp;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        this.AssociatedObject.PreviewMouseDown -= OnMouseDown;
        this.AssociatedObject.PreviewMouseMove -= OnMouseMove;
        this.AssociatedObject.PreviewMouseUp -= OnMouseUp;
    }

    private void OnMouseDown(object sender, MouseButtonEventArgs e)
    {
        var proxy = GetProxy(this);
        if (proxy != null)
        {
            var pos = e.GetPosition(this.AssociatedObject);
            var args = new MouseCaptureArgs {
                X = pos.X,
                Y = pos.Y,
                LeftButton = (e.LeftButton == MouseButtonState.Pressed),
                RightButton = (e.RightButton == MouseButtonState.Pressed)
            };
            proxy.OnMouseDown(this, args);
        }
    }

    private void OnMouseMove(object sender, MouseEventArgs e)
    {
        var proxy = GetProxy(this);
        if (proxy != null)
        {
            var pos = e.GetPosition(this.AssociatedObject);
            var args = new MouseCaptureArgs {
                X = pos.X,
                Y = pos.Y,
                LeftButton = (e.LeftButton == MouseButtonState.Pressed),
                RightButton = (e.RightButton == MouseButtonState.Pressed)
            };
            proxy.OnMouseMove(this, args);
        }
    }

    private void OnMouseUp(object sender, MouseButtonEventArgs e)
    {
        var proxy = GetProxy(this);
        if (proxy != null)
        {
            var pos = e.GetPosition(this.AssociatedObject);
            var args = new MouseCaptureArgs
            {
                X = pos.X,
                Y = pos.Y,
                LeftButton = (e.LeftButton == MouseButtonState.Pressed),
                RightButton = (e.RightButton == MouseButtonState.Pressed)
            };
            proxy.OnMouseUp(this, args);
        }
    }

}

要使用此行为,请将其添加到目标 UI 元素并绑定(bind)到实现代理接口(interface)的对象。在这种情况下,我让 MainViewModel 实现了接口(interface),所以我只是绑定(bind)到它:
<!-- Canvas must have a background, even if it's Transparent -->
<Canvas Background="White" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity">
    <i:Interaction.Behaviors>
        <behaviors:MouseCaptureBehavior Proxy="{Binding}" />
    </i:Interaction.Behaviors>

View 模型现在需要提供行为将调用的鼠标处理程序,它还需要提供行为将在 View 模型引发时响应的捕获/释放事件:
public class MainViewModel : ViewModelBase, IMouseCaptureProxy
{
    public event EventHandler Capture;
    public event EventHandler Release;

    public void OnMouseDown(object sender, MouseCaptureArgs e) {...}
    public void OnMouseMove(object sender, MouseCaptureArgs e) {...}
    public void OnMouseUp(object sender, MouseCaptureArgs e) {...}
}

更新:这应该是不言而喻的,但以防万一:您传递给 Capture 和 Release 事件的发送者应该与您通过 MouseDown/Move/Up 处理程序收到的发送者相同。未使用传递给 Capture/Receive 的事件参数,可以为 null。

关于c# - 使用 MVVM 相对于 WPF 中图像的鼠标位置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34984093/

相关文章:

c# - 如何将 Int[][] 转换为 Double[][]?

wpf - 在 F# 中处理事件

c# - ReactiveUI ViewModel 内存泄漏

c# - ChangePropertyAction 修改可见性在 ListBoxItem DataTemplate 中不起作用

c# - 如何使用公共(public)跟踪标识?

c# - 正在读取 Azure Blob 内容,同时通过 CopyBlob API 启动复制

c# - 如何使用 Entity Framework 7 在代码优先方法中处理这两个模型?

c# - 关于构建操作的澄清

c# - 'clr-namespace' URI 指的是未包含在程序集中的命名空间

c# - 使用 Style 将 Calendar 的宽度更改为其父 DatePicker 的宽度