wpf keyboard-shortcuts

wpf - Definición de accesos directos de MenuItem



keyboard-shortcuts (5)

Necesito una manera simple de establecer un atajo para los elementos del menú.

Pero esto no funciona con atajo, solo con un clic:

<MenuItem Header="Editar"> <MenuItem Header="Procurar" Name="MenuProcurar" InputGestureText="Ctrl+F" Click="MenuProcurar_Click"> <MenuItem.ToolTip> <ToolTip> Procurar </ToolTip> </MenuItem.ToolTip> </MenuItem> </MenuItem>

Estoy usando WPF 4.0


En mi humilde opinión es mucho más fácil simplemente usar _ en el encabezado. Esto creará automáticamente la tecla de acceso rápido deseada.

Por ejemplo:

<MenuItem Header="_Editar"> <MenuItem Header="_Procurar" Name="MenuProcurar" InputGestureText="Ctrl+F" Click="MenuProcurar_Click"> <MenuItem.ToolTip> <ToolTip> Procurar </ToolTip> </MenuItem.ToolTip> </MenuItem> </MenuItem>


Estoy demasiado predispuesto por Windows.Forms & gulp VB 6, así que estoy de acuerdo con Jonathan y Jase que debe haber un método más directo / de procedimiento para conectar estáticamente controladores de eventos que no sean necesariamente CommandBindings . Y hay, creo.

Un buen tutorial para el uso de controladores CommandBinding como este, pero con énfasis en los botones, se puede encontrar en esta publicación de blog de MSDN , creo. Voy a destilar y apuntar a MenuItem s ...

Creando el ICommand

Primero, crea una clase implementando ICommand . Puede poner esto en cualquier lugar, por supuesto, incluso en su archivo MainWindow.xaml.cs si lo desea, para mantener su código de demostración increíblemente simple. Probablemente quiera hacer que CanExecute más complicado cuando desee CanExecute / seleccionar elementos del menú más adelante, pero por ahora, siempre tendremos habilitados nuestros elementos de menú.

public class HelloWorldCommand : ICommand { public void Execute(object parameter) { MessageBox.Show(@"""Hello, world!"" from " + (parameter ?? "somewhere secret").ToString()); } public bool CanExecute(object parameter) { return true; } public event EventHandler CanExecuteChanged; }

Como lo señala amablemente el tutorial, ya puede llamar a este comando desde cualquier lugar, con un código como ...

var hwc = new HelloWorldCommand(); if (hwc.CanExecute(this)) hwc.Execute(this);

Declarando su comando en la ventana

Así que agreguemos una especie de "declaración" para HelloWorldCommand a nuestra ventana para que podamos usarla más adelante. Dentro de tus etiquetas de Window , registra el comando como un recurso:

<Window.Resources> <local:HelloWorldCommand x:Key="hwc"/> </Window.Resources>

Ahora tenemos un atajo ordenado para enlazar a este comando "local namespaced", "hwc" , aunque obviamente puede usar cualquier cadena que desee. Lo usaremos mucho en nuestro xaml.

Conexión (¡y reutilización!) Del comando

MenuItem nuestros MenuItem a nuestro xaml. He reemplazado el Grid DockPanel con un DockPanel porque esa es la forma más fácil (para mí) de tener widgets equiespaciados que llenan la Window , aunque he dejado todo el resto de mi UI.

Tenga en cuenta que Command="{StaticResource hwc}" rociado en cada declaración de MenuItem . La clave es el hwc : recuerde que ese es nuestro atajo para HelloWorldCommand que configuramos en el nivel de la Window . Y, por supuesto, StaticResource dice solo para buscar los recursos de la Window . No estamos vinculando nada; solo estamos usando nuestro atajo.

<DockPanel LastChildFill="True"> <Menu DockPanel.Dock="Top"> <MenuItem Header="_File"> <MenuItem Header="_Open" Command="{StaticResource hwc}" > <MenuItem.CommandParameter> <!-- so you could make this object as complex as you wanted, like, say, your entire Window. See magic incantation, below. --> <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType=Window}" /> </MenuItem.CommandParameter> </MenuItem> <MenuItem Header="_Close" Command="{StaticResource hwc}" CommandParameter="Close" InputGestureText="Ctrl+G" /> <MenuItem Header="_Save" Command="{StaticResource hwc}" CommandParameter="Save" /> <Separator /> <MenuItem Header="_Quit" Command="{StaticResource hwc}" CommandParameter="Quit" /> </MenuItem> </DockPanel>

CommandParameters para distinguir las fuentes de eventos

Tenga en cuenta que estamos utilizando el mismo comando para todo! Pero, ¿cómo podemos saber qué widget lanzó el evento? Para eso, necesita usar el CommandParameter : recuerde la firma de nuestro método Execute(object parameter) : Execute(object parameter) . Ese parámetro CommandParameter es lo que podemos usar para saber cómo manejar el evento. Intente ejecutar esto y tenga en cuenta que MessageBox usará lo que esté en CommandParameter para permitirle conocer el origen del evento. Lo hacemos todo de forma manual, pero eso no es tan malo.

También tenga en cuenta que puede hacer que estos objetos sean tan complicados como desee. Puede usar una propiedad en la etiqueta MenuItem para definir el parámetro, o puede usar etiquetas "reales" <MenuItem.CommandParameter> , como en el elemento de menú Abrir, arriba, para definir algo complejo. En este caso, estamos pasando todo el objeto de Window primario , que fue la forma más fácil (aunque no la más limpia) de convertir nuestro contexto VB6-ish en el código del controlador de eventos.

Agregar atajos de teclado a MenuItem s (también conocido como "Responder al OP")

¡Y ahora finalmente podemos responder la pregunta original! Finalmente, conectemos un atajo de teclado para el elemento del menú Close . InputGestureText que ya hemos declarado un InputGestureText . Por sí mismo, InputGestureText es solo cosmético . Si fuéramos demasiado exigentes, podríamos argumentar que el mecanismo para crear el atajo de teclado no tiene ninguna relación directa e inherente con MenuItem .

Necesitamos en su lugar (o adicionalmente) registrar un oyente para Ctrl-G en el nivel de Window para capturar la pulsación de tecla. Entonces, en el nivel superior de sus etiquetas de ventana, inserte esto (tomado esencialmente de aquí ):

<Window.InputBindings> <KeyBinding Modifiers="Control" Key="G" Command="{StaticResource hwc}" CommandParameter="window input binding" /> </Window.InputBindings>

Tenga en cuenta que podría colocar etiquetas CommandParameter en su KeyBinding moviéndolo de una pieza de XML de cierre KeyBinding etiquetas "abiertas" y KeyBinding " KeyBinding " "reales".

Y terminamos. Ejecute su aplicación y presione Ctrl-G. Whaddup.

Bastante sencillo, una vez que tienes jugadores claros, y mucho menos magia vinculante que la mayoría de las introducciones a los comandos y MenuItems , creo.

Posible consejo profesional:

Todo el asunto de CommandBinding me confundió por un tiempo. Esto es solo para tipos de comando específicos, creo. Es decir, no puedes simplemente conectar cualquier Command te guste. Cosas como las que se jactan aquí (¡en lo que es un tutorial de introducción digno!) ...

Puede que no sea completamente obvio, pero al usar comandos, obtenemos un montón de cosas gratis: Atajos de teclado, texto y Texto de entrada de texto en los ítems y WPF automáticamente habilita / deshabilita los ítems dependiendo del control activo y su estado. En este caso, Cortar y Copiar están deshabilitados porque no se seleccionó ningún texto, pero Pegar está habilitado, ¡porque mi portapapeles no está vacío!

... es algo mágico y no necesariamente bueno, y puede ser confuso cuando eres nuevo en los menús de WPF.


HB tenía razón ... solo quería agregar más precisiones.

Elimine el evento Click en su MenuItem y MenuItem con un Command lugar.

1 - Agregar / crear tus comandos:

<Window.CommandBindings> <CommandBinding Command="Open" Executed="OpenCommandBinding_Executed"></CommandBinding> <CommandBinding Command="SaveAs" Executed="SaveAsCommandBinding_Executed"></CommandBinding> </Window.CommandBindings>

Los comandos hacen referencia al siguiente código

private void OpenCommandBinding_Executed(object sender, ExecutedRoutedEventArgs e) { Open();//Implementation of open file } private void SaveAsCommandBinding_Executed(object sender, ExecutedRoutedEventArgs e) { SaveAs();//Implementation of saveAs }

2 - Asociar los comandos con las claves deseadas:

<Window.InputBindings> <KeyBinding Key="O" Modifiers="Control" Command="Open"></KeyBinding> <KeyBinding Key="S" Modifiers="Control" Command="SaveAs"></KeyBinding> </Window.InputBindings>

3 - Finalmente asigne los comandos con su elemento de menú (InpuGestureText es solo un texto de decoración):

<Menu Name="menu1" > <MenuItem Header="_File" > <MenuItem Name="menuOpen" Header="_Open..." Command="Open" InputGestureText="Ctrl+O" /> <MenuItem Name="menuSaveAs" Header="_Save as..." Command="SaveAs" InputGestureText="Ctrl+S" /> </MenuItem> </Menu>

De esta forma, múltiples entradas pueden estar asociadas al mismo comando.


Necesita utilizar KeyBindings (y CommandBindings si (re) usa RoutedCommands como los que se encuentran en la clase ApplicationCommands ) para eso en los controles donde deberían funcionar los accesos directos.

p.ej

<Window.CommandBindings> <CommandBinding Command="New" Executed="CommandBinding_Executed" /> </Window.CommandBindings> <Window.InputBindings> <KeyBinding Key="N" Modifiers="Control" Command="New"/> </Window.InputBindings>

Para RoutedCommands personalizados:

static class CustomCommands { public static RoutedCommand DoStuff = new RoutedCommand(); }

uso:

<Window ... xmlns:local="clr-namespace:MyNamespace"> <Window.CommandBindings> <CommandBinding Command="local:CustomCommands.DoStuff" Executed="DoStuff_Executed" /> </Window.CommandBindings> <Window.InputBindings> <KeyBinding Key="D" Modifiers="Control" Command="local:CustomCommands.DoStuff"/> </Window.InputBindings> ... </Window>

(A menudo es más conveniente implementar la interfaz ICommand en lugar de usar RoutedCommands . Puede tener un constructor que tome delegados para Execute y CanExecute para crear fácilmente comandos que hacen cosas diferentes, tales implementaciones a menudo se llaman DelegateCommand o RelayCommand . De esta manera lo hace no necesita CommandBindings ).


También puede declarar RoutedUICommand en XAML:

<Window.Resources> <RoutedUICommand x:Key="BuildCmd" Text="Build"> <RoutedUICommand.InputGestures> <KeyGesture>CTRL+SHIFT+B</KeyGesture> </RoutedUICommand.InputGestures> </RoutedUICommand> </Window.Resources>

Hacer la unión

<Window.CommandBindings> <CommandBinding Command="{StaticResource BuildCmd}" Executed="BuildCmdExecuted"/> </Window.CommandBindings>

Y en MenuItem

<MenuItem Command="{StaticResource BuildCmd}"/>

Otra solución se discute here .