Reputation: 404
I'm trying to add a shourcut to my MenuItem:
<Menu x:Name="menu" VerticalAlignment="Top">
<Menu.InputBindings>
<KeyBinding Modifiers="Control" Key="N" Command="{Binding NewTerrainCommand}" CommandParameter="{Binding ElementName=Viewport3D, Path=Camera}" />
</Menu.InputBindings>
<MenuItem Header="_File">
<MenuItem Header="_New Terrain" Command="{Binding NewTerrainCommand}" CommandParameter="{Binding ElementName=Viewport3D, Path=Camera}" InputGestureText="Ctrl+N" />
</MenuItem>
<MenuItem Header="_Edit" />
<MenuItem Header="_Window" />
<MenuItem Header="_Help" />
</Menu>
I read that this can be done by defining a keybinding but that just brought me more questions:
and still is not working, I'm pretty sure I'm missing something.
Thank you.
Upvotes: 0
Views: 61
Reputation: 1173
A little explanation first:
Think of a Command (in this instance a RoutedCommand
, or in general and implementation of ICommand
) as a signal of sorts that propagates through the Visual tree. At each element of the visual tree, WPF checks to see whether there is a specific action that is registered against that command, and if one is found, that action is executed.
In your example, there are three different associations that need to be declared:
MenuItem
needs to be associated with a CommandCanExecute
, Executed
)MenuItem
needs an association with Ctrl+N
key combination. Accomplishing (#1) is straightforward - you simply specify the command as a binding in the MenuItem
.
(#2) is more interesting. Commands, as I mentioned earlier, are meant to relay a signal for action through the Visual tree. This implies that different parts of the visual tree would have to declare their respective capability to process that Command. This declaration is made by means of a CommandBinding
. A CommandBinding
associates a Command with actions (CanExecute
, Executed
) in the context of a visual entity.
Supposing that the method to execute our Command would live in the main Window
class (typically, MainWindow
), we'd need to specify a CommandBinding
on the MainWindow
instance. (To be more precise, MainWindow
would be one of the types that declares it's ability to process the said Command. It is conceivable that other types declare an interest in the same Command
- our following example just happens to have only one type expressing interest in handing the Command).
Once this association is declared (between MainWindow and the Command in question), any time the Command is triggered (say, by a MenuItem
), WPF would know to invoke the registered CanExecute
and Executed
methods in MainWindow
, because a corresponding CommandBinding
exists to declare the fact that MainWindow
is able to handle the Command.
(#3) is relatively easy to do as well. RoutedCommand
has an InputGestures
property, and a KeyGesture
can be added to it. Once added, the MenuItem
that is associated with this Command will automatically infer the KeyGesture
.
The following example (largely derived from your own code, with modifications) shows how this is all wired together:
MainWindow.xaml:
<Window x:Class="CommandsSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:system_windows_input="clr-namespace:System.Windows.Input;assembly=PresentationCore"
xmlns:local="clr-namespace:CommandsSample"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<!-- The following DataContext declaration simply means that the current instance of MainWindow (i.e., 'this')
is the operative DataContext for all data bindings specified in the rest of this XAML -->
<Window.DataContext>
<Binding RelativeSource="{RelativeSource Self}"/>
</Window.DataContext>
<Window.Resources>
<!-- Declare a RoutedCommand with an associated KeyGesture -->
<RoutedCommand x:Key="NewFileCommand">
<RoutedCommand.InputGestures>
<system_windows_input:KeyGesture>
Ctrl+N
</system_windows_input:KeyGesture>
</RoutedCommand.InputGestures>
</RoutedCommand>
</Window.Resources>
<!-- The following CommandBinding associates NewFileCommand with actions defined in the code behind -->
<Window.CommandBindings>
<CommandBinding Command="{StaticResource NewFileCommand}" CanExecute="CouldCreateNewFile" Executed="CreatenewFile"/>
</Window.CommandBindings>
<!-- Main UI consisting of a Menu and a TextBox -->
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="_File">
<!-- The New MenuItem will automatically infer the KeyGesture Ctrl+N from NewFileCommand -->
<MenuItem Header="_New" Command="{StaticResource NewFileCommand}" />
<MenuItem Header="_Open"/>
<MenuItem Header="_Save"/>
<Separator/>
<MenuItem Header="_Exit"/>
</MenuItem>
</Menu>
<TextBox AcceptsReturn="True" Text="{Binding FileText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DockPanel>
</Window>
MainWindow.xaml.cs:
using System;
using System.Windows;
using System.Windows.Input;
namespace CommandsSample
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
#region NewFileCommand Handlers
/// <summary>
/// CanExecute
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void CouldCreateNewFile(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = !string.IsNullOrEmpty(FileText);
}
/// <summary>
/// Executed
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void CreatenewFile(object sender, ExecutedRoutedEventArgs e)
{
FileText = string.Empty;
}
#endregion NewFileCommand Handlers
#region Dependency Properties
/// <summary>
/// The contents of the TextBox in the UI are backed by this property
/// </summary>
public String FileText
{
get { return (String)GetValue(FileTextProperty); }
set { SetValue(FileTextProperty, value); }
}
// Using a DependencyProperty as the backing store for FileText. This enables animation, styling, binding, etc...
public static readonly DependencyProperty FileTextProperty =
DependencyProperty.Register("FileText", typeof(String), typeof(MainWindow), new PropertyMetadata(string.Empty));
#endregion Dependency Properties
}
}
Upvotes: 1