2011-12-23 22 views
5

Todos los menús/contextmenus/barras de herramientas que utilizo en WPF se declaran en el código modelo de vista más o menos así:Métodos abreviados de teclado de MenuItem en MVVM 'puro'

MenuService.Add(new MenuItem() 
    { 
    Header = "DoStuff", 
    Command = new relayCommand(DoStuff,() => CanDoStuffExecute()) 
    // some more properties like parent item/image/... 
    }); 

El Menuservice proporciona un único punto de unión que es una lista jerárquica de Menultem y se une al Menú real de ItemsSource en xaml.

Esto funciona muy bien y ahora me gustaría añadir atajos de teclado de la misma manera conveniente. Idealmente Menultem obtendría una propiedad de tipo System.Windows.Input.KeyGesture por lo que simplemente puede escribir

Shortcut = new KeyGesture(Key.D, ModifierKeys.Control) 

lo que resultaría en el Comando del elemento que se llama al golpear Ctrl + D en la ventana que posee el menú, y que también llevar a mostrar automáticamente "Ctrl + D" en el menú.

Sin embargo estoy perdido aquí: quería establecer la colección MenuItem.InputBindings a través de enlace de datos, pero es conseguir-solamente. ¿Cómo puedo obtener artículos de todos modos? ¿O hay un marco MVVM que ya sea compatible con algo como esto? La mayoría de los q & que encontré en los atajos de teclado tratan sobre la configuración de accesos directos a través de xaml, lo que no sirve de nada.

actualización

Buscando 'RelayCommand vs routeduicommand y 'RelayCommand keygesture' etc reveló suficiente información para llegar a una solución de trabajo a pesar de hacky. Definitivamente hay otras y mejores formas de hacerlo, pero de momento esto es una prioridad muy baja para mí y hace el trabajo a la perfección. He añadido dos propiedades a la clase MenuItem como esto:

//Upon setting a Gesture, the original command is replaced with a RoutedCommand 
//since that automatically gives us proper display of the keyboard shortcut. 
//The RoutedCommand simply calls back into the original Command. 
//It also sets the CommandBinding property, which still has to be added to the 
//CommandBindingCollection of either the bound control or one of it ancestors 
public InputGesture Gesture 
{ 
    set 
    { 
    var origCmd = Command; 
    if(origCmd == null) 
     return; 
    var routedCmd = new RoutedCommand(Header, 
     typeof(System.Windows.Controls.MenuItem), 
     new InputGestureCollection { value }); 
    CommandBinding = new CommandBinding(routedCmd, 
     (sender, args) => origCmd.Execute(args.Parameter), 
     (sender, args) => { args.CanExecute = origCmd.CanExecute(args.Parameter); }); 
    Command = routedCmd; 
    } 
} 

//CommandBinding created when setting Gesture 
public CommandBinding CommandBinding { get; private set; } 

Así que esto da la funcionalidad pedí originalmente (es decir, añadir atajos de teclado en el código donde sean fácilmente configurable, etc.). Todo lo que queda es registrar los enlaces de comando. Por el momento, esto se hace simplemente agregando todos ellos a Application.Current.MainWindow.CommandBindings.

Respuesta

3

Esto realmente no califica como una 'respuesta' (no puedo agregar un comentario evidentemente) - pero sugiero que lo que está haciendo, no es el método previsto en WPF. Estás haciendo esto de la forma de Windows Forms (y como en muchos otros kits de herramientas), definiendo tu UX en el código. Has llegado tan lejos como lo hiciste, pero ahora te has topado con una pared de ladrillos: los gestos clave son puramente UX, definitivamente no se deben especificar en código subyacente. La apariencia (como una función del modelo de vista) y la interacción del usuario con ella (formas de hacer que un comando determinado suceda) son para la definición de XAML.

valor de las propiedades y comandos son para su vista-modelo, por lo que se puede reutilizar este punto de vista-modelo para otros puntos de vista, y también fácil crear unidad de pruebas para ello. ¿De qué manera la implementación de los atajos de teclado en el modelo de vista ayuda a la capacidad de prueba? Y para usar en otras vistas, se podría argumentar que los accesos directos reales podrían no aplicarse a una nueva vista, por lo que no es allí donde pertenecen. Puede que tenga otros motivos, por supuesto, pero le sugiero que considere la posibilidad de definirlos en XAML.

-Se ha añadido, en respuesta a su comentario-

Tiene usted razón - y he visto algunos bastante grandes proyectos WPF UX que intentaron difícil de evitar cualquier código y terminó innecesariamente obtuso. Intento simplemente usar cualquier enfoque que produzca un resultado de trabajo, y es tan simple como puedo obtenerlo.

Aquí hay un fragmento de ejemplo que simplemente crea la Menultem ..

<Menu x:Name="miMain" DockPanel.Dock="Top"> 
    <MenuItem Command="{Binding Path=MyGreatCommand}" Header="DoSomething"/> 

Eso crea el menú. Aquí, MyGreatCommand es ICommand, y es simplemente una propiedad en el modelo de vista. Yo por lo general dentro de un lugar que DockPanel, manejar la StatusBar, etc.

Para asignar el gesto clave ..

<Window.InputBindings> 
    <KeyBinding Key="X" Modifiers="ALT" Command="{Binding Path=MyGreatCommand}"/> 

Sin embargo, ya que ha mencionado que ya ha buscado respuestas y sólo se encuentran XAML - Supongo que ya has probado este enfoque. He usado RoutedUICommand s en lugar de definido por el usuario ICommand s, para obtener ese bonito gesto de la tecla alineado a la derecha en el texto del encabezado, pero no he encontrado cómo hacer las dos cosas. Si insiste en crear los comandos y los gestos de teclas en el código, puede que tenga que crear RoutedUICommand s.

¿Por qué quieres configurar los gestos de teclas de forma distinta a tu XAML?

Si desea que algunos elementos de menú que aparecen sólo cuando ciertos estados tienen influencia dentro de su vista-modelo, entonces se puede enlazar la propiedad Visibility de un elemento de menú (que puede contener otros elementos de menú) para Collapsed o Visible .

+0

¿Puede proporcionarnos algún ejemplo de código? ¿Quiere decir que debería definir el menú completo en xaml? ¿O solo el atajo? Me gustaría ver cómo ... Necesito elementos como un menú global de "Herramientas" en el que los complementos pueden agregar submenús a voluntad, ¿eso se puede hacer incluso en xaml? También supongo que puede, sería un poco basura el código con xaml, ¿no? Ahora todo lo que necesito para agregar un comando de menú es el código como se muestra. Eso es lo más corto y práctico que puede llegar y funciona maravillosamente. Por cierto, tu muro de ladrillos funciona en dos direcciones: todavía tengo que ver una aplicación a gran escala donde no se define ninguna pieza de UX en el código ... Eso es un mito. – stijn

+0

Tiene toda la razón, y he visto algunos proyectos WPF UX bastante grandes que intentaban realmente evitar cualquier código, y terminaron innecesariamente obtusos. Intento simplemente usar el enfoque que sea más simple. Aquí hay un fragmento de la muestra (perdón por la falta de finese - estoy seguro de cómo dar formato a este en este sitio web) .. ' JamesWHurst

+1

edición su respuesta, pegue el código, selecciónelo y haga clic en el botón * Muestra de código *. – stijn

Cuestiones relacionadas