使用MVVM在WPF中相对于图像的鼠标位置

时间:2016-01-25 02:10:48

标签: 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>

视图模型:

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

    }
}

我想主要的是如何从ImageOnDisplay内访问视图元素ImagePositionViewModel

2 个答案:

答案 0 :(得分:2)

我这样做有一个行为。首先,我声明一个我的视图模型将实现的接口:

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元素并绑定到实现代理接口的对象。在这种情况下,我让MainViewModel实现了接口,所以我只是绑定到它:

<!-- 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>

视图模型现在需要提供行为将调用的鼠标处理程序,它还需要提供捕获/释放事件,该行为将在视图模型引发时响应:

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的事件args未被使用,可以为null。

答案 1 :(得分:0)

使用MVVM鼠标在图像上移动时加载图像并显示其xy坐标和rgb值的解决方案.. 希望这对某些人来说是完全有用的。

视图模型:

class MainWindowViewModel : ViewModelBase
{
    private Bitmap Img; 
    public ICommand OpenImg { get; set; }
    public MainWindowViewModel()
    {
        OpenImg = new RelayCommand(openImg, (obj) => true);
    }       
    private void openImg(object obj = null)
    {
        OpenFileDialog op = new OpenFileDialog();
        op.Title = "Select a picture";
        op.Filter = "All supported graphics|*.jpg;*.jpeg;*.png;*.bmp;*.tiff|" +
          "JPEG (*.jpg;*.jpeg)|*.jpg;*.jpeg|" +
          "Portable Network Graphic (*.png)|*.png";
        if (op.ShowDialog() == true)
        {
            ImgPath = op.FileName;
            Img = new Bitmap(ImgPath);
        }
    }

    private string _ImgPath;
    public string ImgPath
    {
        get
        {
            return _ImgPath;
        }
        set
        {
            _ImgPath = value;
            OnPropertyChanged("ImgPath");
        }
    }

    private ICommand _mouseMoveCommand;
    public ICommand MouseMoveCommand
    {
        get
        {
            if (_mouseMoveCommand == null)
            {
              _mouseMoveCommand = new RelayCommand(param => ExecuteMouseMove((MouseEventArgs)param));
            }      
            return _mouseMoveCommand;
        }
        set { _mouseMoveCommand = value; }
    }
    private void ExecuteMouseMove(MouseEventArgs e)
    {
        System.Windows.Point p = e.GetPosition(((IInputElement)e.Source));
        XY = String.Format("X: {0} Y:{1}", (int)p.X, (int)p.Y);

        BitmapData bd = Img.LockBits(new Rectangle(0, 0, Img.Width, Img.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
        unsafe
        {
            byte* ptr = (byte*)bd.Scan0;
            int x = (int)p.X * 3;
            int y = (int)p.Y * bd.Stride;
            RGB = "R: "+ptr[x + y + 2].ToString() + " G: " + ptr[x + y + 1].ToString() + " B: " + ptr[x + y].ToString();
        }
        Img.UnlockBits(bd);
    }
    private string xy;
    public string XY
    {
        get { return xy; }
        set
        {
            xy = value;
            OnPropertyChanged("XY");
        }
    }
    private string rgb;
    public string RGB
    {
        get { return rgb; }
        set
        {
            rgb = value;
            OnPropertyChanged("RGB");
        }
    }
}

MainWindow.xaml

<Window.Resources>
    <vm:MainWindowViewModel x:Key="MainWindowViewModel"/>
</Window.Resources>
<Grid DataContext="{StaticResource MainWindowViewModel}">
    <Grid.RowDefinitions>
        <RowDefinition Height="30"/>
        <RowDefinition Height="*" />
        <RowDefinition Height="25"/>
    </Grid.RowDefinitions>
    <Grid Grid.Row="0">
        <Menu FontSize="20">
            <MenuItem Header="File">
                <MenuItem Header="Open" Command="{Binding OpenImg}"/>
            </MenuItem>
        </Menu>
    </Grid>
    <Grid Grid.Row="1" Background="LightGray">
        <Viewbox Margin="3,3,3,3">
                    <Image x:Name="img" Stretch="None" Source="{Binding ImgPath}"
                           Model:MouseBehaviour.MouseMoveCommand="{Binding MouseMoveCommand}">     
                    </Image>
        </Viewbox>
    </Grid>
    <Grid Grid.Row="2">
        <StackPanel Orientation="Horizontal">
            <TextBox Focusable="False" Text="{Binding XY}" Width="100"/>
            <TextBox Focusable="False" Text="{Binding RGB}" Width="115"/>
        </StackPanel>
    </Grid>
</Grid>