Все меню / контекстные меню / панели инструментов, которые я использую в wpf, объявлены в коде ViewModel примерно так:
MenuService.Add( new MenuItem()
{
Header = "DoStuff",
Command = new relayCommand( DoStuff, () => CanDoStuffExecute() )
// some more properties like parent item/image/...
} );
MenuService предоставляет единую точку привязки, которая представляет собой иерархический список MenuItem и привязывается к фактическому ItemsSource меню в xaml.
Это работает очень хорошо, и теперь я хотел бы добавить сочетания клавиш таким же удобным способом.
В идеале MenuItem получит свойство типа System.Windows.Input.KeyGesture
, поэтому я могу просто написать
Shortcut = new KeyGesture( Key.D, ModifierKeys.Control )
, что приведет к тому, что команда вызываемого элемента будет вызвана при нажатии Ctrl + D в окне, которому принадлежит меню, что также приведет к автоматическому отображению в меню «Ctrl + D».
Однако здесь я заблудился: я хотел установить коллекцию MenuItem.InputBindings через привязку данных, но она предназначена только для получения. Как я могу в любом случае положить туда предметы? Или есть фреймворк MVVM, который уже поддерживает что-то подобное? Большинство вопросов и ответов, которые я обнаружил по сочетаниям клавиш, связаны с их настройкой через xaml, что бесполезно.
Обновление
Поиск «relaycommand vs routeduicommand» и «relaycommand keygesture» и т. Д. Дал достаточно информации, чтобы придумать работающее, хотя и хакерское решение. Определенно есть другие и лучшие способы, но на данный момент для меня это очень низкий приоритет, и он отлично справляется со своей задачей.Я добавил два свойства к классу MenuItem следующим образом:
//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; }
Таким образом, это дает функциональность, о которой я просил изначально (т.е. добавление сочетаний клавиш в код, где они легко настраиваются и т. Д.). Осталось только зарегистрировать привязки команд. На данный момент это делается простым добавлением их всех в Application.Current.MainWindow.CommandBindings.