wpf 如何在我的视图模型中获取鼠标位置

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30047415/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 13:07:28  来源:igfitidea点击:

How do I get mouse positions in my view model

c#wpfmvvm

提问by Epitouille

From MVVM Design pattern, the viewmodel should not know the view. But in my case, I need the view and the model, I mean :

从 MVVM 设计模式来看,视图模型不应该知道视图。但就我而言,我需要视图和模型,我的意思是:

In my window, I've an Image component. I'd like to get mouse position when mouse moves over the Image component and save it into my model.

在我的窗口中,我有一个 Image 组件。我想在鼠标移到 Image 组件上时获取鼠标位置并将其保存到我的模型中。

The code behind would have been :

背后的代码是:

void Foo_MouseMove(objet sender, MouseEventArgs e)
{
  model.x = e.getPosition(this.imageBox).X; 
  model.y = e.getPosition(this.imageBox).Y;
}

The problem is : I need this.imageBox and MouseEventArgs, so two View element.

问题是:我需要 this.imageBox 和 MouseEventArgs,所以有两个 View 元素。

My question is : How to deal with this case using the MVVM approach ?

我的问题是:如何使用 MVVM 方法处理这种情况?

I use MVVM light framework

我使用 MVVM 轻量级框架

回答by Mark Green

I would use an attached behaviour here. This will allow you to continuously monitor the mouse position, rather than simply responding to an event such as MouseDown. You'll need to add a reference to the System.Windows.Interactivityassembly.

我会在这里使用附加的行为。这将允许您持续监视鼠标位置,而不是简单地响应诸如 MouseDown 之类的事件。您需要添加对System.Windows.Interactivity程序集的引用。

The code below provides a simple example of this in action.

下面的代码提供了一个简单的例子。

XAML

XAML

<Window x:Class="MouseMoveMvvm.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        xmlns:mouseMoveMvvm="clr-namespace:MouseMoveMvvm"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <DockPanel>
            <StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
                <TextBlock Text="{Binding PanelX, StringFormat='X={0}'}" />
                <TextBlock Text="{Binding PanelY, StringFormat='y={0}'}" />
            </StackPanel>
            <Canvas DockPanel.Dock="Bottom" Background="Aqua">
                <i:Interaction.Behaviors>
                    <mouseMoveMvvm:MouseBehaviour MouseX="{Binding PanelX, Mode=OneWayToSource}" MouseY="{Binding PanelY, Mode=OneWayToSource}" />
                </i:Interaction.Behaviors>
            </Canvas>
        </DockPanel>
    </Grid>
</Window>

Note that, in the above XAML, the MouseBehaviour is pushing the mouse position down to the ViewModel through a OneWayToSource binding, while the two TextBlocks are reading the mouse positions from the ViewModel.

请注意,在上面的 XAML 中,MouseBehaviour 通过 OneWayToSource 绑定将鼠标位置向下推到 ViewModel,而两个 TextBlock 正在从 ViewModel 读取鼠标位置。

ViewModel

视图模型

public class MainWindowViewModel : INotifyPropertyChanged
{
    private double _panelX;
    private double _panelY;
    public event PropertyChangedEventHandler PropertyChanged;

    public double PanelX
    {
        get { return _panelX; }
        set
        {
            if (value.Equals(_panelX)) return;
            _panelX = value;
            OnPropertyChanged();
        }
    }

    public double PanelY
    {
        get { return _panelY; }
        set
        {
            if (value.Equals(_panelY)) return;
            _panelY = value;
            OnPropertyChanged();
        }
    }


    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

Attached Behaviour

附加行为

public class MouseBehaviour : System.Windows.Interactivity.Behavior<FrameworkElement>
{
    public static readonly DependencyProperty MouseYProperty = DependencyProperty.Register(
        "MouseY", typeof (double), typeof (MouseBehaviour), new PropertyMetadata(default(double)));

    public double MouseY
    {
        get { return (double) GetValue(MouseYProperty); }
        set { SetValue(MouseYProperty, value); }
    }

    public static readonly DependencyProperty MouseXProperty = DependencyProperty.Register(
        "MouseX", typeof(double), typeof(MouseBehaviour), new PropertyMetadata(default(double)));

    public double MouseX
    {
        get { return (double) GetValue(MouseXProperty); }
        set { SetValue(MouseXProperty, value); }
    }

    protected override void OnAttached()
    {
        AssociatedObject.MouseMove += AssociatedObjectOnMouseMove;
    }

    private void AssociatedObjectOnMouseMove(object sender, MouseEventArgs mouseEventArgs)
    {
        var pos = mouseEventArgs.GetPosition(AssociatedObject);
        MouseX = pos.X;
        MouseY = pos.Y;
    }

    protected override void OnDetaching()
    {
        AssociatedObject.MouseMove -= AssociatedObjectOnMouseMove;
    }
}

回答by Epitouille

Finnally found an answer, using a EventConverter :

最终使用 EventConverter 找到了答案:

  public class MouseButtonEventArgsToPointConverter : IEventArgsConverter
    {
        public object Convert(object value, object parameter)
        {
            var args = (MouseEventArgs)value;
            var element = (FrameworkElement)parameter;
            var point = args.GetPosition(element);
            return point;
        }
    }

This converter allows me to deal with Point and not with graphics components.

这个转换器允许我处理 Point 而不是图形组件。

Here goes the XML :

这是 XML :

        <i:Interaction.Triggers>
            <i:EventTrigger EventName="MouseMove">
                <cmd:EventToCommand
                 Command="{Binding Main.MouseMoveCommand, Mode=OneWay}"
                 EventArgsConverter="{StaticResource MouseButtonEventArgsToPointConverter}"
                 EventArgsConverterParameter="{Binding ElementName=Image1}"
                 PassEventArgsToCommand="True" />
            </i:EventTrigger>
        </i:Interaction.Triggers>

回答by Julian

Mark Greens solution is the best (I found).

Mark Greens 解决方案是最好的(我发现)。

If you want to make his solution reusable for any WPF control (which I suggest), inheriting from System.Windows.Interactivity.Behavior<Control>actually won't work for Panel, because Paneldoes not inherit from Control. Only those classes inherit from Control: https://msdn.microsoft.com/de-de/library/system.windows.controls.control(v=vs.110).aspx

如果你想让他的解决方案可重用于任何 WPF 控件(我建议),继承自System.Windows.Interactivity.Behavior<Control>实际上不会为 工作Panel,因为Panel不继承自Control. 只有那些类继承自Controlhttps: //msdn.microsoft.com/de-de/library/system.windows.controls.control(v=vs.110).aspx

Instead, inherit from System.Windows.Interactivity.Behavior<FrameworkElement>. FrameworkElementis the ancestor of all WPF control classes: https://msdn.microsoft.com/de-de/library/system.windows.frameworkelement(v=vs.110).aspx. I have tested it on Grid, Paneland Imagebtw.

相反,从System.Windows.Interactivity.Behavior<FrameworkElement>. FrameworkElement是所有 WPF 控件类的祖先:https: //msdn.microsoft.com/de-de/library/system.windows.frameworkelement(v=vs.110).aspx。我已经测试过它GridPanel并且Image顺便说一句。

I use it to keep a Popup in sync with the mouse cursor:

我用它来使弹出窗口与鼠标光标保持同步:

<Image x:Name="Image1">        
  <i:Interaction.Behaviors>
    <myNamespace:MouseBehaviour
      MouseX="{Binding ElementName=Popup1, Path=HorizontalOffset, Mode=OneWayToSource}"
      MouseY="{Binding ElementName=Popup1, Path=VerticalOffset, Mode=OneWayToSource}">
    </myNamespace:MouseBehaviour>
  </i:Interaction.Behaviors>
</Image>

<Popup x:Name="Popup1" PlacementTarget="{Binding ElementName=Image1}"/>

P.S.: I would have commented on the solution, but my answer is too long.

PS:我会对解决方案发表评论,但我的回答太长了。