MouseBinding колесико мыши для масштабирования WPF и MVVM
-
20-09-2019 - |
Вопрос
Хорошо, я понял, как масштабировать сетку элементов пользовательского интерфейса с помощью LayoutTransform и ScaleTransform.Чего я не понимаю, так это того, как я могу заставить свое представление реагировать на CTRL+MouseWheelUp\Down, чтобы сделать это, и как вписать код в шаблон MVVM.
Моя первая идея заключалась в том, чтобы сохранить ZoomFactor как свойство и привязать его к команде для его настройки.
Я смотрел что-то вроде:
<UserControl.InputBindings>
<MouseBinding Command="{Binding ZoomGrid}" Gesture="Control+WheelClick"/>
</UserControl.InputBindings>
но я вижу 2 проблемы:
1) Я не думаю, что есть способ определить, было ли колесо перемещено вверх или вниз, и я не знаю, как определить, насколько.Я видел MouseWheelEventArgs.Delta, но понятия не имею, как его получить.
2) Привязка к команде в модели представления кажется неправильной, поскольку это строго представление.
Поскольку масштабирование предназначено исключительно для просмотра пользовательского интерфейса, я думаю, что фактический код должен находиться в коде программной части.
Как бы вы, ребята, это реализовали?
p.s. я использую .net\wpf 4.0, используя Cinch для MVVM.
Решение
Я бы предложил вам реализовать общую команду масштабирования на вашей виртуальной машине.Команду можно параметризовать новым уровнем масштабирования или (возможно, даже проще) реализовать Команда увеличения масштаба и Команда УменьшениеМасштаба.Затем используйте код представления для вызова этих команд после обработки аргументов события колеса мыши.Если дельта положительна, увеличьте масштаб, если отрицательная, уменьшите.
Нет ничего плохого в том, чтобы решить эту проблему, используя несколько строк кода.Основная идея MVVM заключается в том, что вы можете отслеживать и изменять почти полное состояние вашего представления в объекте, который не зависит от пользовательского интерфейса (повышает тестируемость).Как следствие, расчет нового окна просмотра, являющегося результатом масштабирования, должен выполняться в виртуальной машине, а не в коде.
Небольшой пробел в тестируемости, существующий в коде, можно либо игнорировать, либо покрыть автоматическими тестами пользовательского интерфейса.Однако автоматические тесты пользовательского интерфейса могут быть очень дорогими.
Другие советы
реальный ответ - написать свой собственный MouseGesture, что несложно.
<MouseBinding Gesture="{x:Static me:MouseWheelGesture.CtrlDown}"
Command="me:MainVM.SendBackwardCommand" />
public class MouseWheelGesture : MouseGesture
{
public static MouseWheelGesture CtrlDown
=> new MouseWheelGesture(ModifierKeys.Control) { Direction = WheelDirection.Down
public MouseWheelGesture(): base(MouseAction.WheelClick)
{
}
public MouseWheelGesture(ModifierKeys modifiers) : base(MouseAction.WheelClick, modifiers)
{
}
public WheelDirection Direction { get; set; }
public override bool Matches(object targetElement, InputEventArgs inputEventArgs)
{
if (!base.Matches(targetElement, inputEventArgs)) return false;
if (!(inputEventArgs is MouseWheelEventArgs args)) return false;
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>
Модель просмотра:
ZoomCommand = new RelayCommand<RoutedEventArgs>(Zoom);
...
public void Zoom(RoutedEventArgs e)
{
var originalEventArgs = e as MouseWheelEventArgs;
// originalEventArgs.Delta contains the relevant value
}
Я надеюсь, что это помогает кому-то.Я знаю, что вопрос довольно старый...
Я думаю, что то, что вы пытаетесь сделать, во многом связано с представлением, поэтому нет никакого вреда в размещении кода в вашем коде (по крайней мере, по моему мнению), хотя я уверен, что есть элегантные способы справиться с этим, так что это больше основано на модели представления.
У вас должна быть возможность зарегистрироваться в событии OnPrevewMouseWheel, проверить, нажал ли пользователь клавишу управления, и соответствующим образом изменить коэффициент масштабирования, чтобы получить желаемый эффект масштабирования.
Я согласен с обоими ответами и хотел бы лишь добавить, что использование кода — единственный способ в этом случае, поэтому вам даже не придется думать о том, нарушает ли это какие-либо передовые практики или нет.
Дело в том, что единственный способ получить MouseEventArgs (и, следовательно, Delta) находится в коде, поэтому возьмите там то, что вам нужно (для этого не требуется никакой логики), и передайте это в свою модель представления, как предложил Олли.
С другой стороны, вы можете использовать более общую дельту (например,разделите его на 120, прежде чем передавать его в качестве шага к модели представления), чтобы он не учитывал какие-либо соглашения, связанные с представлением или ОС.Это позволит максимально повторно использовать ваш код в модели представления.
Чтобы избежать всей проблемы, есть еще один вариант:- используйте ContentPresenter в xaml и привяжите его содержимое к объекту модели представления.-обработка событий колеса мыши в модели представления.