I tried all kinds of approaches using XAML and nothing worked. I finally found a solution based on the answer proved by Shahid Neermunda
First, the menu bar:
<Menu x:Name="MainMenuBar" Grid.Row="0" HorizontalContentAlignment="Left">
<MenuItem Header="_File" HorizontalContentAlignment="Left">
<MenuItem x:Name="NewProjectMenuItem"
Header="New Project"
InputGestureText="Ctrl+N"
Click="NewProject_Click"/>
<MenuItem x:Name="OpenProjectMenuItem"
Header="Open Project"
InputGestureText="Ctrl+O"
Click="OpenProject_Click"/>
<MenuItem x:Name="CloseProjectMenuItem"
Header="Close Project"
Click="CloseProject_Click"/>
<Separator/>
<MenuItem x:Name="SaveProjectMenuItem"
Header="Save Project"
InputGestureText="Ctrl+S"
Click="SaveProject_Click"/>
<MenuItem x:Name="SaveProjectAsMenuItem"
Header="Save Project As ..."
InputGestureText="Shift+Ctrl+S"
Click="SaveProjectAs_Click"/>
<Separator/>
<MenuItem x:Name="ExitMenuItem"
Header="Exit"
InputGestureText="Alt+F4"
Click="Exit_Click"/>
</MenuItem>
</Menu>
Nothing fancy. Each menu item has an 'InputGestureText' property (except for the close)
I then modified the click event methods that were auto-generated by the Click="[tab]" command. I'm only showing two here--one where there's a shortcut key defined and another where it isn't (Close):
private void OpenProject_Executed(object sender, ExecutedRoutedEventArgs e) => OpenProject_Click(sender, e);
private void OpenProject_Click(object sender, RoutedEventArgs e)
{
OpenProject();
}
private void CloseProject_Click(object sender, RoutedEventArgs e)
{
CloseProject();
}
The XXX_Executed(...) method is called by the shortcut binding (which I'll get to next) and the XXX_Click method is called by the Click command.
I did the same for the New Project, Open Project, Save Project As, and Exit auto-generated XXX_Click methods.
I then created a new file with the bindings (I separated it out to make it easier to find when the time comes to add additional bindings):
partial class MainWindow
{
private void BindShortcuts()
{
BindShortcut(Key.N, ModifierKeys.Control, NewProject_Executed);
BindShortcut(Key.O, ModifierKeys.Control, OpenProject_Executed);
BindShortcut(Key.S, ModifierKeys.Control, SaveProject_Executed);
BindShortcut(Key.S, ModifierKeys.Control | ModifierKeys.Shift, SaveProjectAs_Executed);
BindShortcut(Key.F4, ModifierKeys.Alt, Exit_Executed);
}
private void BindShortcut(Key key, ModifierKeys modifiers, ExecutedRoutedEventHandler executed)
{
RoutedCommand cmd = new();
_ = cmd.InputGestures.Add(new KeyGesture(key, modifiers));
_ = CommandBindings.Add(new CommandBinding(cmd, executed));
}
}
This way, when I add new menu items with more shortcuts attached, I only need to add the appropriate <MenuItem .../> tag, define an XXX_Executed method to call into the auto-generated XXX_Click method, and update the BindShortcuts() function.
Finally, I added the following to my constructor for the MainWindow class:
public MainWindow()
{
InitializeComponent();
BindShortcuts();
}
Works like a charm.