teclas teclado tamaño submenus sirve rapidas que por para métodos metodos imagen illustrator hacer descargar defecto cs6 crear control combinaciones comandos codigos cambiar basicas atajos abreviados wpf keyboard-shortcuts menuitem key-bindings

wpf - tamaño - ¿Cómo mostrar el método abreviado de teclado de trabajo para los elementos del menú?



teclas para cambiar tamaño imagen photoshop (4)

Así es como lo hizo:

En el evento cargado de mi ventana, coincido con los enlaces de Comando de los elementos del menú con los enlaces de Comando de todos los InputBindings, muy parecido a la respuesta de ethicallogics, pero para una barra de menú y realmente compara los enlaces del Comando y no solo el valor, porque no funciono para mi este código también se repite en submenús .

private void MainWindow_OnLoaded(object sender, RoutedEventArgs e) { // add InputGestures to menu items SetInputGestureTextsRecursive(MenuBar.Items, InputBindings); } private void SetInputGestureTextsRecursive(ItemCollection items, InputBindingCollection inputBindings) { foreach (var item in items) { var menuItem = item as MenuItem; if (menuItem != null) { if (menuItem.Command != null) { // try to find an InputBinding with the same command and take the Gesture from there foreach (KeyBinding keyBinding in inputBindings.OfType<KeyBinding>()) { // we cant just do keyBinding.Command == menuItem.Command here, because the Command Property getter creates a new RelayCommand every time // so we compare the bindings from XAML if they have the same target if (CheckCommandPropertyBindingEquality(keyBinding, menuItem)) { // let a new Keygesture create the String menuItem.InputGestureText = new KeyGesture(keyBinding.Key, keyBinding.Modifiers).GetDisplayStringForCulture(CultureInfo.CurrentCulture); } } } // recurse into submenus if (menuItem.Items != null) SetInputGestureTextsRecursive(menuItem.Items, inputBindings); } } } private static bool CheckCommandPropertyBindingEquality(KeyBinding keyBinding, MenuItem menuItem) { // get the binding for ''Command'' property var keyBindingCommandBinding = BindingOperations.GetBindingExpression(keyBinding, InputBinding.CommandProperty); var menuItemCommandBinding = BindingOperations.GetBindingExpression(menuItem, MenuItem.CommandProperty); if (keyBindingCommandBinding == null || menuItemCommandBinding == null) return false; // commands are the same if they''re defined in the same class and have the same name return keyBindingCommandBinding.ResolvedSource == menuItemCommandBinding.ResolvedSource && keyBindingCommandBinding.ResolvedSourcePropertyName == menuItemCommandBinding.ResolvedSourcePropertyName; }

Haga esto una vez en el código detrás de su ventana y cada elemento del menú tiene un InputGesture. Solo falta la traducción

Estoy tratando de crear una barra de menú de WPF localizable con elementos de menú que tengan métodos abreviados de teclado, no teclas / mnemónicas aceleradoras (que generalmente se muestran como caracteres subrayados que se pueden presionar para seleccionar directamente un elemento de menú cuando el menú ya está abierto), pero métodos abreviados de teclado (normalmente combinaciones de Ctrl + otra tecla ) que se muestran alineadas a la derecha junto al encabezado del elemento del menú.

Estoy usando el patrón MVVM para mi aplicación, lo que significa que evito colocar cualquier código en el código subyacente siempre que sea posible y tengo mis modelos de vista (que asigno a las propiedades de DataContext ) proporcionan implementaciones de la interfaz ICommand que usan los controles en mis opiniones

Como base para reproducir el problema, aquí hay un código fuente mínimo para una aplicación como se describe:

Window1.xaml

<Window x:Class="MenuShortcutTest.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MenuShortcutTest" Height="300" Width="300"> <Menu> <MenuItem Header="{Binding MenuHeader}"> <MenuItem Header="{Binding DoSomethingHeader}" Command="{Binding DoSomething}"/> </MenuItem> </Menu> </Window>

Window1.xaml.cs

using System; using System.Windows; namespace MenuShortcutTest { public partial class Window1 : Window { public Window1() { InitializeComponent(); this.DataContext = new MainViewModel(); } } }

MainViewModel.cs

using System; using System.Windows; using System.Windows.Input; namespace MenuShortcutTest { public class MainViewModel { public string MenuHeader { get { // in real code: load this string from localization return "Menu"; } } public string DoSomethingHeader { get { // in real code: load this string from localization return "Do Something"; } } private class DoSomethingCommand : ICommand { public DoSomethingCommand(MainViewModel owner) { if (owner == null) { throw new ArgumentNullException("owner"); } this.owner = owner; } private readonly MainViewModel owner; public event EventHandler CanExecuteChanged; public void Execute(object parameter) { // in real code: do something meaningful with the view-model MessageBox.Show(owner.GetType().FullName); } public bool CanExecute(object parameter) { return true; } } private ICommand doSomething; public ICommand DoSomething { get { if (doSomething == null) { doSomething = new DoSomethingCommand(this); } return doSomething; } } } }

La clase de WPF MenuItem tiene una propiedad InputGestureText , pero como se describe en SO preguntas como this , this , this y this , eso es puramente cosmético y no tiene ningún efecto sobre qué atajos son procesados ​​realmente por la aplicación.

Entonces, preguntas como this y this indican que el comando debe estar vinculado con un KeyBinding en la lista InputBindings de la ventana. Si bien eso habilita la funcionalidad, no muestra automáticamente el acceso directo con el elemento del menú. Window1.xaml cambia de la siguiente manera:

<Window x:Class="MenuShortcutTest.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MenuShortcutTest" Height="300" Width="300"> <Window.InputBindings> <KeyBinding Key="D" Modifiers="Control" Command="{Binding DoSomething}"/> </Window.InputBindings> <Menu> <MenuItem Header="{Binding MenuHeader}"> <MenuItem Header="{Binding DoSomethingHeader}" Command="{Binding DoSomething}"/> </MenuItem> </Menu> </Window>

He intentado configurar manualmente la propiedad InputGestureText además, haciendo que Window1.xaml se vea así:

<Window x:Class="MenuShortcutTest.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MenuShortcutTest" Height="300" Width="300"> <Window.InputBindings> <KeyBinding Key="D" Modifiers="Control" Command="{Binding DoSomething}"/> </Window.InputBindings> <Menu> <MenuItem Header="{Binding MenuHeader}"> <MenuItem Header="{Binding DoSomethingHeader}" Command="{Binding DoSomething}" InputGestureText="Ctrl+D"/> </MenuItem> </Menu> </Window>

Esto muestra el acceso directo, pero no es una solución viable por razones obvias:

  • No se actualiza cuando cambia el enlace de acceso directo real, por lo que incluso si los accesos directos no son configurables por los usuarios, esta solución es una pesadilla de mantenimiento.
  • El texto debe ser localizado (como, por ejemplo, la tecla Ctrl tiene diferentes nombres en algunos idiomas), por lo que si se cambia alguno de los accesos directos, todas las traducciones deberán actualizarse individualmente.

He estudiado la creación de un IValueConverter para usar para vincular la propiedad InputGestureText a la lista InputBindings de la ventana (puede haber más de un KeyBinding en la lista InputBindings , o ninguno en absoluto, por lo que no hay una instancia específica de KeyBinding que pueda vincular a (si KeyBinding incluso se presta a ser un objetivo vinculante)). Esto me parece la solución más deseable, porque es muy flexible y al mismo tiempo muy limpia (no requiere una gran cantidad de declaraciones en varios lugares), pero por un lado, InputBindingCollection no implementa INotifyCollectionChanged , por lo tanto, el enlace no se actualizaría cuando se sustituyan los accesos directos, y por otro lado, no logré proporcionar al convertidor una referencia a mi modelo de vista de forma ordenada (que necesitaría para acceder a los datos de localización). Además, InputBindings no es una propiedad de dependencia, por lo que no puedo vincularlo a una fuente común (como una lista de enlaces de entrada ubicados en el modelo de vista) a la que la propiedad ItemGestureText podría estar vinculada.

Ahora, muchos recursos ( this , esa pregunta , este hilo , esa pregunta y ese hilo señalan que RoutedCommand y RoutedUICommand contienen una propiedad InputGestures de InputGestures e implican que los enlaces de teclas de esa propiedad se muestran automáticamente en los elementos del menú.

Sin embargo, el uso de cualquiera de las implementaciones de ICommand parece abrir una nueva lata de gusanos, ya que sus métodos Execute y CanExecute no son virtuales y, por lo tanto, no se pueden anular en las subclases para completar la funcionalidad deseada. La única forma de proporcionar eso parece ser declarar un CommandBinding en XAML (que se muestra, por ejemplo, this o this ) que conecta un comando con un controlador de eventos; sin embargo, ese controlador de eventos se ubicará en el código subyacente, violando así la arquitectura MVVM descrito arriba.

Sin embargo, al intentarlo, esto significa convertir la mayor parte de la estructura antes mencionada (lo que también implica que debo decidir cómo resolver eventualmente el problema en mi etapa actual de desarrollo, comparativamente temprana):

Window1.xaml

<Window x:Class="MenuShortcutTest.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:MenuShortcutTest" Title="MenuShortcutTest" Height="300" Width="300"> <Window.CommandBindings> <CommandBinding Command="{x:Static local:DoSomethingCommand.Instance}" Executed="CommandBinding_Executed"/> </Window.CommandBindings> <Menu> <MenuItem Header="{Binding MenuHeader}"> <MenuItem Header="{Binding DoSomethingHeader}" Command="{x:Static local:DoSomethingCommand.Instance}"/> </MenuItem> </Menu> </Window>

Window1.xaml.cs

using System; using System.Windows; namespace MenuShortcutTest { public partial class Window1 : Window { public Window1() { InitializeComponent(); this.DataContext = new MainViewModel(); } void CommandBinding_Executed(object sender, System.Windows.Input.ExecutedRoutedEventArgs e) { ((MainViewModel)DataContext).DoSomething(); } } }

MainViewModel.cs

using System; using System.Windows; using System.Windows.Input; namespace MenuShortcutTest { public class MainViewModel { public string MenuHeader { get { // in real code: load this string from localization return "Menu"; } } public string DoSomethingHeader { get { // in real code: load this string from localization return "Do Something"; } } public void DoSomething() { // in real code: do something meaningful with the view-model MessageBox.Show(this.GetType().FullName); } } }

DoSomethingCommand.cs

using System; using System.Windows.Input; namespace MenuShortcutTest { public class DoSomethingCommand : RoutedCommand { public DoSomethingCommand() { this.InputGestures.Add(new KeyGesture(Key.D, ModifierKeys.Control)); } private static Lazy<DoSomethingCommand> instance = new Lazy<DoSomethingCommand>(); public static DoSomethingCommand Instance { get { return instance.Value; } } } }

Por la misma razón ( RoutedCommand.Execute y el hecho de no ser virtual), no sé cómo subclase RoutedCommand de una manera para crear un RelayCommand como el que se usa en una respuesta a esta pregunta basada en RoutedCommand , por lo que no tengo para desviarse en los InputBindings de la ventana, mientras se reimplementan explícitamente los métodos de ICommand en una subclase RoutedCommand siente como si pudiera estar rompiendo algo.

Además, si bien el acceso directo se muestra automáticamente con este método según lo configurado en RoutedCommand , no parece que se localice automáticamente. Mi entendimiento es que agregando

System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("de-de"); System.Threading.Thread.CurrentThread.CurrentUICulture = System.Threading.Thread.CurrentThread.CurrentCulture;

MainWindow constructor de MainWindow debe asegurarse de que las cadenas localizables proporcionadas por el marco se tomen de CultureInfo alemán. Sin embargo, Ctrl no cambia a Strg , así que, a menos que me equivoque sobre cómo configurar CultureInfo para las cadenas proporcionadas por el marco, este método no es viable de todos modos si espero que el atajo mostrado se localice correctamente.

Ahora, soy consciente de que KeyGesture me permite especificar una cadena de visualización personalizada para el método abreviado de teclado, pero no solo la clase DoSomethingCommand RoutedCommand DoSomethingCommand desconectada de todas mis instancias (desde donde podría contactar con la localización cargada) debido En la forma en que CommandBinding debe vincularse con un comando en XAML, la propiedad DisplayString respectiva es de solo lectura, por lo que no habría forma de cambiarlo cuando se cargue otra localización en tiempo de ejecución.

Esto me deja con la opción de excavar manualmente a través del árbol de menú (EDITAR: para una aclaración, no hay código aquí porque no estoy preguntando por esto y sé cómo hacerlo) y la lista InputBindings de la ventana para verificar cuál los comandos tienen cualquier instancia de KeyBinding asociada a ellos, y los elementos del menú están vinculados a cualquiera de esos comandos, de modo que puedo configurar manualmente el InputGestureText de cada uno de los respectivos elementos del menú para reflejar el primero (o preferido, según la métrica que desee) use aquí) el atajo de teclado. Y este procedimiento debería repetirse cada vez que creo que los enlaces de teclas pueden haber cambiado. Sin embargo, esto parece una solución extremadamente tediosa para algo que es esencialmente una característica básica de una GUI de la barra de menú, por lo que estoy convencido de que no puede ser la forma "correcta" de hacerlo.

¿Cuál es la forma correcta de mostrar automáticamente un método abreviado de teclado que está configurado para funcionar con las instancias de Artículos de MenuItem WPF?

EDITAR: Todas las otras preguntas que encontré se referían a cómo se podría usar un KeyBinding / KeyGesture para habilitar realmente la funcionalidad visualmente implícita en InputGestureText , sin explicar cómo vincular automáticamente los dos aspectos en la situación descrita. La única pregunta un tanto prometedora que encontré fue this , pero no ha recibido ninguna respuesta en más de dos años.


Basado en la respuesta de Pavel Voronin, creé lo siguiente. En realidad, acabo de crear dos nuevos UserControls que configuran automáticamente Gesture en el comando y lo leen.

class HotMenuItem : MenuItem { public HotMenuItem() { SetBinding(InputGestureTextProperty, new Binding("Command.GestureText") { Source = this }); } } class HotKeyBinding : KeyBinding { protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) { base.OnPropertyChanged(e); if (e.Property.Name == "Command" || e.Property.Name == "Gesture") { if (Command is IHotkeyCommand hotkeyCommand) hotkeyCommand.Gesture = Gesture as KeyGesture; } } }

La interfaz utilizada

public interface IHotkeyCommand { KeyGesture Gesture { get; set; } }

El comando es bastante similar, solo implementa INotifyPropertyChanged .

Así que el uso se vuelve un poco más limpio en mi opinión:

<Window.InputBindings> <viewModels:HotKeyBinding Command="{Binding ExitCommand}" Gesture="Alt+F4" /> </Window.InputBindings> <Menu> <MenuItem Header="File" > <viewModels:HotMenuItem Header="Exit" Command="{Binding ExitCommand}" /> </MenuItem> </Menu>


Si no he entendido mal tu pregunta, prueba esto:

<Window.InputBindings> <KeyBinding Key="A" Modifiers="Control" Command="{Binding ClickCommand}"/> </Window.InputBindings> <Grid > <Button Content="ok" x:Name="button"> <Button.ContextMenu> <local:CustomContextMenu> <MenuItem Header="Click" Command="{Binding ClickCommand}"/> </local:CustomContextMenu> </Button.ContextMenu> </Button> </Grid>

..con:

public class CustomContextMenu : ContextMenu { public CustomContextMenu() { this.Opened += CustomContextMenu_Opened; } void CustomContextMenu_Opened(object sender, RoutedEventArgs e) { DependencyObject obj = this.PlacementTarget; while (true) { obj = LogicalTreeHelper.GetParent(obj); if (obj == null || obj.GetType() == typeof(Window) || obj.GetType() == typeof(MainWindow)) break; } if (obj != null) SetInputGestureText(((Window)obj).InputBindings); //UnSubscribe once set this.Opened -= CustomContextMenu_Opened; } void SetInputGestureText(InputBindingCollection bindings) { foreach (var item in this.Items) { var menuItem = item as MenuItem; if (menuItem != null) { for (int i = 0; i < bindings.Count; i++) { var keyBinding = bindings[i] as KeyBinding; //find one whose Command is same as that of menuItem if (keyBinding!=null && keyBinding.Command == menuItem.Command)//ToDo : Apply check for None Modifier menuItem.InputGestureText = keyBinding.Modifiers.ToString() + " + " + keyBinding.Key.ToString(); } } } } }

Espero que esto te dé una idea.


Voy a empezar con la advertencia. Puede suceder que no solo necesite teclas de acceso rápido personalizables, sino también el propio menú. Así que piense dos veces antes de usar InputBindings estáticamente.
Hay una precaución más con respecto a InputBindings : implican que el comando está vinculado al elemento en el árbol visual de la ventana. A veces necesitas teclas de acceso rápido globales que no están conectadas con ninguna ventana en particular.

Lo dicho anteriormente significa que puede hacerlo de otra manera e implementar el procesamiento de gestos de su propia aplicación con el enrutamiento correcto a los comandos correspondientes (no olvide utilizar referencias débiles a los comandos).

No obstante, la idea de los comandos conscientes de los gestos es la misma.

public class CommandWithHotkey : ICommand { public bool CanExecute(object parameter) { return true; } public void Execute(object parameter) { MessageBox.Show("It Worked!"); } public KeyGesture Gesture { get; set; } public string GestureText { get { return Gesture.GetDisplayStringForCulture(CultureInfo.CurrentUICulture); } } public string Text { get; set; } public event EventHandler CanExecuteChanged; public CommandWithHotkey() { Text = "Execute Me"; Gesture = new KeyGesture(Key.K, ModifierKeys.Control); } }

Modelo de vista simple:

public class ViewModel { public ICommand Command { get; set; } public ViewModel() { Command = new CommandWithHotkey(); } }

Ventana:

<Window x:Class="CommandsWithHotKeys.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:commandsWithHotKeys="clr-namespace:CommandsWithHotKeys" Title="MainWindow" Height="350" Width="525"> <Window.DataContext> <commandsWithHotKeys:ViewModel/> </Window.DataContext> <Window.InputBindings> <KeyBinding Command="{Binding Command}" Key ="{Binding Command.Gesture.Key}" Modifiers="{Binding Command.Gesture.Modifiers}"></KeyBinding> </Window.InputBindings> <Grid> <Menu HorizontalAlignment="Stretch" VerticalAlignment="Top" Height="Auto"> <MenuItem Header="Test"> <MenuItem InputGestureText="{Binding Command.GestureText}" Header="{Binding Command.Text}" Command="{Binding Command}"> </MenuItem> </MenuItem> </Menu> </Grid> </Window>

Claro, de alguna manera debes cargar la información de gestos desde la configuración y luego iniciar los comandos con los datos.

El siguiente paso es hacer keystokes como en VS: Ctrl + K, Ctrl + D, la búsqueda rápida da a esta pregunta SO .