2015-05-05 3 views
3

Из шаблона проектирования MVVM модель представления не должна знать вид. Но в моем случае мне нужен вид и модель, я имею в виду:Как получить позиции мыши в моей модели модели

В моем окне у меня есть компонент изображения. Я хотел бы получить положение мыши, когда мышь перемещается по компоненту Image и сохраняет его в моей модели.

код позади был бы:

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

Проблема заключается в том: Мне нужно this.imageBox и MouseEventArgs, поэтому два элемента View.

Мой вопрос: как бороться с этим случаем, используя подход MVVM?

Я использую MVVM light framework

+0

* Мне нужно this.imageBox и MouseEventArgs, так что два элемента View * ... если они являются элементами вида, а затем обрабатывают их в коде вида, независимо от того, используете ли вы MVVM или нет. Элементы представления do * not * относятся к модели представления. – Sheridan

ответ

5

Finnally нашел ответ, используя 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; 
     } 
    } 

Этот конвертер позволяет мне иметь дело с точки, а не с графическими компонентами ,

Здесь идет 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> 
4

Я бы использовал приложенное поведение здесь. Это позволит вам постоянно контролировать положение мыши, а не просто реагировать на событие, такое как MouseDown. Вам нужно будет добавить ссылку на сборку System.Windows.Interactivity.

Приведенный ниже код дает простой пример этого в действии.

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> 

Обратите внимание, что в приведенном выше XAML, то MouseBehaviour толкает позицию мыши вниз к ViewModel через OneWayToSource связывания, в то время как два TextBlocks читают позиции мыши из 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)); 
    } 
} 

Прикрепленный Поведение

public class MouseBehaviour : System.Windows.Interactivity.Behavior<Panel> 
{ 
    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; 
    } 
} 
+1

Это должно быть приемлемым ответом, как для меня. Правильно ли я говорю, что это необязательно должно быть элементом управления холстом, это может быть любой элемент управления, которым вы хотите контролировать координаты, даже кнопки, если это указано в прикрепленном классе поведения? – AndyUK

+0

Абсолютно. Код, который я написал, должен правильно иметь поведение, наследующее от System.Windows.Interactivity.Behavior , а не System.Windows.Interactivity.Behavior , потому что MouseMove является свойством Control, но я оставлю Panel в своем ответе, потому что это больше тесно связан с этим вопросом. –

2

решение Марк Greens является лучшим (я нашел).

Если вы хотите, чтобы сделать его решение повторно использоваться для любого элемента управления WPF (которые я предлагаю), унаследовав от System.Windows.Interactivity.Behavior<Control> фактически не будет работать для Panel, потому что Panel не наследует от Control. только те классы наследуют от Control: https://msdn.microsoft.com/de-de/library/system.windows.controls.control(v=vs.110).aspx

Вместо наследоваться от System.Windows.Interactivity.Behavior<FrameworkElement>. FrameworkElement является предком всех классов управления WPF: https://msdn.microsoft.com/de-de/library/system.windows.frameworkelement(v=vs.110).aspx. Я проверил его на Grid, Panel и Image кстати.

Я использую его, чтобы сохранить Popup синхронно с курсором мыши:

<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 .: Я бы прокомментировал решение, но мой ответ слишком долго.

Смежные вопросы