Привязка мыши к колесу мыши для увеличения WPF и MVVM

хорошо, я понял, как получить мою сетку элементов пользовательского интерфейса для увеличения, используя LayoutTransform и ScaleTransform. Я не понимаю, как я могу заставить свое представление реагировать на CTRL+MouseWheelUpDown, чтобы сделать это, и как вписать код в шаблон MVVM.

моей первой идеей было сохранить ZoomFactor как свойство и привязаться к команде для его настройки.

Я смотрел на что-то вроде:

<UserControl.InputBindings>
 <MouseBinding Command="{Binding ZoomGrid}" Gesture="Control+WheelClick"/>
</UserControl.InputBindings>

но я вижу 2 проблемы:

1) I не думайте, что есть способ определить, было ли колесо перемещено вверх или вниз, и я не вижу, как определить, насколько. Я видел MouseWheelEventArgs.Дельта, но понятия не имею, как ее достать.

2) привязка к команде на viewmodel не кажется правильной, так как это строго видовая вещь.

Так как масштабирование строго UI View только, я думаю,что фактический код должен идти в коде позади.

как бы вы это реализовали?

p.s., Я ... использование .netwpf 4.0 с помощью Cinch для MVVM.

6 ответов


Я бы предложил вам реализовать общую команду масштабирования в вашей виртуальной машине. Команда может быть параметризована с новым уровнем масштабирования или (возможно, даже проще) вы можете реализовать IncreaseZoomCommand и DecreaseZoomCommand. Затем используйте код представления, чтобы вызвать эти команды после обработки аргументов события колеса мыши. Если дельта положительная, увеличьте, если отрицательная-уменьшите.

нет никакого вреда в решении этой проблема с использованием нескольких строк кода позади. Основная идея MVVM заключается в том, что вы можете отслеживать и изменять почти полное состояние вашего представления в объекте, который не зависит от пользовательского интерфейса (повышает тестируемость). Следовательно, вычисление нового окна просмотра, которое является результатом масштабирования, должно выполняться в виртуальной машине, а не в коде позади.

небольшой разрыв тестируемости, который существует в коде позади, может быть проигнорирован или покрыт автоматическими тестами UI. Однако автоматические тесты UI могут быть очень дорогими.


реальный anwser должен написать свой собственный MouseGesture, что легко.

<MouseBinding Gesture="{x:Static me:MouseWheelGesture.CtrlDown}"  
              Command="me:MainVM.SendBackwardCommand" />






 public class MouseWheelGesture : MouseGesture
  {
    public WheelDirection Direction { get; set; }


    public static MouseWheelGesture CtrlDown
    {
      get
      {
        return new MouseWheelGesture(ModifierKeys.Control) { Direction = WheelDirection.Down };
      }
    }


    public MouseWheelGesture(): base(MouseAction.WheelClick)
    {
    }

    public MouseWheelGesture(ModifierKeys modifiers) : base(MouseAction.WheelClick, modifiers)
    {
    }



    public override bool Matches(object targetElement, InputEventArgs inputEventArgs)
    {
      if (!base.Matches(targetElement, inputEventArgs)) return false;
      if (!(inputEventArgs is MouseWheelEventArgs)) return false;
      var args = (MouseWheelEventArgs)inputEventArgs;
      switch (Direction)
      {
        case WheelDirection.None:
          return args.Delta == 0;
        case WheelDirection.Up:
          return args.Delta > 0;
        case WheelDirection.Down:
          return args.Delta < 0;
        default:
          return false;
      }
    }



    public enum WheelDirection
    {
      None,
      Up,
      Down,
    }

  }

Если вы не хотите использовать код, вы можете использовать функциональность EventToCommand MVVM light:

вид:

 <...
     xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF4"
 xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 
     ...> 
<i:Interaction.Triggers>
         <i:EventTrigger EventName="PreviewMouseWheel">
             <cmd:EventToCommand Command="{Binding
     Path=DataContext.ZoomCommand,
     ElementName=Root, Mode=OneWay}"
         PassEventArgsToCommand="True"   />
         </i:EventTrigger> </i:Interaction.Triggers>

ViewModel:

ZoomCommand = new RelayCommand<RoutedEventArgs>(Zoom);
...
public void Zoom(RoutedEventArgs e)
{
    var originalEventArgs = e as MouseWheelEventArgs;
    // originalEventArgs.Delta contains the relevant value
}

надеюсь, это кому-то поможет. Я знаю, что вопрос старый...


Я думаю, что то, что вы пытаетесь сделать, очень связано с представлением, поэтому нет никакого вреда в том, чтобы поместить код в ваш код (по крайней мере, на мой взгляд), хотя я уверен, что есть элегантные способы обработки этого, так что он больше основан на viewmodel.

вы должны иметь возможность зарегистрироваться на onprevewmousewheel событие, проверьте, если пользователь получил клавишу управления нажатой и изменить коэффициент масштабирования соответственно, чтобы получить эффект масштабирования вы ищете.


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

факт в том, что единственный способ получить MouseEventArgs (и, следовательно, Дельта) находится в коде позади, поэтому возьмите то, что вам нужно (для этого не нужна логика), и передайте его своей модели представления, как предложил Олли.

с другой стороны, вы можете использовать более общую дельту (например , разделите его на 120, прежде чем передавать его как шаг к модели представления), чтобы держать его в неведении о любых соглашениях, связанных с представлением или ОС. Это позволит максимально использовать код в модели представления.


чтобы избежать всей проблемы, есть еще один вариант : - используйте ContentPresenter в xaml и пусть его содержимое будет привязано к объекту viewmodel. - обработка событий mousewheel в viewmodel.