RatónConectando la rueda del mouse para acercar WPF y MVVM
data-binding cinch (6)
Creo que lo que intentas hacer está muy relacionado con la vista, así que no hay nada de malo en poner código en tu código (al menos en mi opinión), aunque estoy seguro de que hay maneras elegantes de manejarlo de tal manera que es más basado en viewmodel.
Debería poder registrarse en el evento OnPrevewMouseWheel, verificar si el usuario tiene presionada la tecla de control y cambiar su factor de zoom según corresponda para obtener el efecto de zoom que está buscando.
De acuerdo, me he dado cuenta de cómo hacer que mi Cuadrícula de elementos de la interfaz de usuario se acerque, usando LayoutTransform y ScaleTransform. Lo que no entiendo es cómo puedo hacer que mi Vista responda CTRL + MouseWheelUp / Down para hacerlo, y cómo ajustar el código en el patrón de MVVM.
Mi primera idea fue almacenar ZoomFactor como una propiedad y vincularlo a un comando para ajustarlo.
Estaba viendo algo así como:
<UserControl.InputBindings>
<MouseBinding Command="{Binding ZoomGrid}" Gesture="Control+WheelClick"/>
</UserControl.InputBindings>
pero veo 2 problemas:
1) No creo que haya una forma de saber si la rueda se movió hacia arriba o hacia abajo, ni puedo ver cómo determinar cuánto. He visto MouseWheelEventArgs.Delta, pero no tengo idea de cómo conseguirlo.
2) La vinculación a un comando en el modelo de vista no parece correcta, ya que es estrictamente una cosa de Vista.
Dado que el zoom es estrictamente solo UI View, creo que el código real debería ir en el código subyacente.
¿Cómo implementarían esto?
pd, estoy usando .net / wpf 4.0 usando Cinch para MVVM.
Sugeriría que implemente un comando de zoom genérico en su máquina virtual. El comando puede parametrizarse con un nuevo nivel de zoom o (quizás incluso más simple) puede implementar un IncreaseZoomCommand y DecreaseZoomCommand . A continuación, utilice el código de la vista para llamar a estos comandos después de haber procesado los argumentos de evento del evento Mouse Wheel. Si el delta es positivo, acerca el zoom, si el zoom es negativo.
No hay problema en resolver este problema usando algunas líneas de código. La idea principal de MVVM es que puede rastrear y modificar casi el estado completo de su vista en un objeto que no depende de la interfaz de usuario (mejora la capacidad de prueba). En consecuencia, el cálculo de la nueva ventana gráfica que es el resultado del zoom se debe hacer en la VM y no en el código subyacente.
La pequeña brecha de capacidad de prueba que existe en el código subyacente puede ser ignorada o cubierta por pruebas automáticas de UI. Las pruebas de IU automáticas, sin embargo, pueden ser muy costosas.
Estoy de acuerdo con ambas respuestas, y solo agregaría que usar código detrás es la única forma en este caso, por lo que ni siquiera tiene que pensar si rompe alguna buena práctica o no.
El hecho es que la única forma de obtener los MouseEventArgs (y por lo tanto el Delta) está en el código subyacente, así que tome lo que necesite allí (no se necesita lógica para eso) y páselo a su modelo de vista como se sugirió anteriormente.
Por otro lado, es posible que desee utilizar un delta más genérico (por ejemplo, divídalo entre 120 antes de pasarlo como un paso hacia el modelo de vista) con el fin de mantenerlo al margen de las convenciones relacionadas con la vista o el sistema operativo. Esto permitirá la reutilización máxima de su código en el modelo de vista.
Si no desea utilizar el código, puede usar la funcionalidad EventToCommand de mvvm light:
Ver:
<...
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
}
Espero que esto ayude a alguien. Sé que la pregunta es algo vieja ...
el verdadero argumento es escribir tu propia MouseGesture, que es fácil.
<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,
}
}
Para evitar todo el problema, hay una opción más: -utilizar un ContentPresenter en el xaml y dejar que su contenido se vincule a un objeto viewmodel. -handle los eventos mousewheel dentro del viewmodel.