Я занимался программированием WPF, и единственное, чего у меня не было, - это шаблон команды. Кажется, что каждый пример предназначен для встроенных: редактировать, вырезать, вставлять. У кого-нибудь есть пример или предложение лучших практик для пользовательских команд?





Ага! Вопрос, на который я могу ответить! Во-первых, я должен упомянуть, что лично мне было проще определять и подключать команды в коде, а не в XAML. Это позволяет мне подключать обработчики команд немного более гибко, чем при использовании XAML-подхода.
Вы должны решить, какие команды вы хотите иметь и к чему они относятся. В моем приложении сейчас есть класс для определения важных команд приложения, например:
public static class CommandBank
{
/// Command definition for Closing a window
public static RoutedUICommand CloseWindow { get; private set; }
/// Static private constructor, sets up all application wide commands.
static CommandBank()
{
CloseWindow = new RoutedUICommand();
CloseWindow.InputGestures.Add(new KeyGesture(Key.F4, ModifierKeys.Alt));
// ...
}
Теперь, поскольку я хотел сохранить весь код вместе, использование подхода к командам только с кодом позволяет мне поместить следующие методы в класс выше:
/// Closes the window provided as a parameter
public static void CloseWindowExecute(object sender, ExecutedRoutedEventArgs e)
{
((Window)e.Parameter).Close();
}
/// Allows a Command to execute if the CommandParameter is not a null value
public static void CanExecuteIfParameterIsNotNull(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = e.Parameter != null;
e.Handled = true;
}
Второй метод может быть использован совместно с другими командами без необходимости повторять его повсюду.
После того, как вы определили такие команды, вы можете добавить их в любую часть пользовательского интерфейса. В дальнейшем, как только Window будет загружен, я добавляю привязки команд как к Window, так и к MenuItem, а затем добавляю привязку ввода к Window, используя цикл, чтобы сделать это для всех привязок команд. Передаваемый параметр - это собственное окно Window, поэтому приведенный выше код знает, какое окно нужно попробовать и закрыть.
public partial class SimpleWindow : Window
{
private void WindowLoaded(object sender, RoutedEventArgs e)
{
// ...
this.CommandBindings.Add(
new CommandBinding(
CommandBank.CloseWindow,
CommandBank.CloseWindowExecute,
CommandBank.CanExecuteIfParameterIsNotNull));
foreach (CommandBinding binding in this.CommandBindings)
{
RoutedCommand command = (RoutedCommand)binding.Command;
if (command.InputGestures.Count > 0)
{
foreach (InputGesture gesture in command.InputGestures)
{
var iBind = new InputBinding(command, gesture);
iBind.CommandParameter = this;
this.InputBindings.Add(iBind);
}
}
}
// menuItemExit is defined in XAML
menuItemExit.Command = CommandBank.CloseWindow;
menuItemExit.CommandParameter = this;
// ...
}
// ....
}
Позже у меня также будут обработчики событий WindowClosing и WindowClosed, я рекомендую вам сделать фактическую реализацию команд как можно более компактной и универсальной. Как и в этом случае, я не пытался поместить код, который пытается остановить закрытие окна, если есть несохраненные данные, я твердо сохранил этот код внутри события WindowClosing.
Дайте мне знать, если у вас возникнут дополнительные вопросы. :)
@Ray Я не знал, что могу это сделать! Спасибо за редактирование, Дрю. :)
Особенность XAML в том, что он подходит для «простых» программ, но, к сожалению, он не работает, когда вы хотите делать такие вещи, как совместное использование функций. Скажем, у вас есть несколько классов и пользовательский интерфейс, у всех из которых есть команды, которые никогда не отключались, вам нужно написать метод CanAlwaysExecute для каждого окна или UserControl! Вот только не очень СУХОЙ.
Прочитав несколько блогов и попробовав несколько вещей, я решил сделать XAML исключительно для внешнего вида, стилей, анимации и триггеров. Все мои подключения обработчиков событий и команд теперь находятся в коде программной части. :)
Кстати, еще одна проблема - это привязка ввода, чтобы их можно было поймать, фокус должен быть на объекте, который содержит привязки ввода. Например, чтобы иметь короткий путь, который вы можете использовать в любое время (скажем, F1, чтобы открыть справку), эта привязка ввода должна быть установлена для объекта Window, поскольку он всегда находится в фокусе, когда ваше приложение активно. Использование метода кода должно упростить это, даже когда вы начинаете использовать UserControls, которые могут захотеть добавить привязки ввода к их родительскому Window.
Я писал о множестве ресурсов по командам WPF вместе с примером в прошлом году на http://blogs.vertigo.com/personal/alanl/Blog/archive/2007/05/31/commands-in-wpf.aspx
Вставка сюда:
Образец главы Адама Натана о важных новых концепциях в WPF: команды
Статья MSDN: Шаблон команды в WPF
Кейван Найери: как добавлять команды в настраиваемый элемент управления WPF
Ян Гриффитс: ввод, команды и обработчики Avalon
Библиотека MSDN: класс CommandBinding
Библиотека MSDN: разделы с инструкциями по вводу и командам
Библиотека MSDN: класс EditingCommands
Библиотека MSDN: класс MediaCommands
Библиотека MSDN: класс ApplicationCommands
Библиотека MSDN: класс NavigationCommands
Библиотека MSDN: класс ComponentCommands
Также в примерах WPF SDK есть хороший пример редактирования RichTextBox, который я расширил. Вы можете найти его здесь: RichTextEditor.zip
В выпуске журнала MSDN за сентябрь 2008 г. Брайан Нойес опубликовал отличную статью о RoutedCommand / RoutedEvents !!!
Ссылка здесь: http://msdn.microsoft.com/en-us/magazine/cc785480.aspx
Ваш CanExecuteIfParameterIsNotNull содержит одно из моих неудобств. Почему бы просто не e.CanExecute = e.Parameter! = Null;