簡體   English   中英

使用MVVM在WPF中相對於圖像的鼠標位置

[英]Mouse Position with respect to image in WPF using 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>

ViewModel:

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

我這樣做是出於行為。 首先,我聲明一個視圖模型將實現的接口:

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>

視圖模型現在需要提供該行為將調用的鼠標處理程序,它還需要提供Capture / Release事件,該行為在視圖模型引發時將響應:

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。

當使用MVVM在圖像上移動鼠標時,加載圖像並使用rgb值顯示其xy坐標的解決方案。希望這對某些人有所幫助。

ViewModel:

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>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM