Небольшое пояснение первое:
Think команды (в данном случае RoutedCommand
, или в целом и реализациях ICommand
) как сигнал, своего рода, который распространяется через дерево визуалов. В каждом элементе визуального дерева WPF проверяет, существует ли конкретное действие, зарегистрированное против этой команды, и если оно найдено, это действие выполняется.
В вашем примере, есть три различных ассоциаций, которые должны быть объявлены:
MenuItem
должна быть связана с командой
- Командование должно быть связано с соответствующими действиями (
CanExecute
, Executed
)
MenuItem
требуется связь с Ctrl+N
комбинация клавиш.
Выполнение (# 1) прост - вы просто указываете команду в качестве привязки в MenuItem
.
(# 2) более интересен. Команды, как я упоминал ранее, предназначены для передачи сигнала для действия через визуальное дерево. Это означает, что разные части визуального дерева должны были бы объявить свою соответствующую способность обрабатывать эту команду. Это заявление сделано с помощью CommandBinding
. A CommandBinding
связывает команду с действиями (CanExecute
, Executed
) в контексте визуального объекта.
Предположив, что метод, чтобы выполнить нашу команду будет жить в главном Window
класса (как правило, MainWindow
), мы должны были бы указать CommandBinding
на экземпляре MainWindow
. (Если быть более точным, MainWindow
был бы одним из типов, объявляющих его способность обрабатывать указанную команду. Возможно, другие типы объявляют интерес к тому же Command
- наш следующий пример просто имеет только один тип, выражающий интерес к передавая команду).
После того, как эта ассоциация объявляются (между MainWindow и командованием в вопросе), в любое время командование срабатывает (например, с помощью MenuItem
), WPF бы знать, чтобы вызвать зарегистрированные CanExecute
и Executed
методы в MainWindow
, поскольку соответствующее CommandBinding
существует, чтобы заявить, что MainWindow
способен обрабатывать команду.
(# 3) относительно легко сделать. RoutedCommand
имеет свойство InputGestures
, и к нему можно добавить KeyGesture
. После добавления MenuItem
, связанный с этой командой, автоматически выдает KeyGesture
.
В следующем примере (в основном получены из вашего собственного кода, с изменениями) показывает, как это все связаны друг с другом:
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
}
}
Создайте 'KeyBinding' против вашего' Window'. WPF начнет с сосредоточенного элемента и поднимет визуальное дерево в поисках 'KeyBinding' – dkozl
@dkozl, поэтому мне нужно определить привязку клавиш к моему окну (в обоих местах) ?, это не так, но что, если datacontext, который имеет ли команда не то же самое, что и datacontext окна? – elios264
Он должен быть где-то вдоль визуального дерева от сосредоточенного элемента, где имеет смысл использовать этот короткий отрезок. Во всех других случаях, когда команда может быть найдена, но параметр неверен, 'CanExecute' должен возвращать false, чтобы отключить команду. – dkozl