Пример настраиваемого шаблона команды WPF

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

Ответов (4)

Решение

Ага! Вопрос, на который я могу ответить! Во-первых, я должен упомянуть, что лично мне было проще определять и подключать команды в коде, а не в 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.

Дайте мне знать, если у вас возникнут дополнительные вопросы. :)

Особенность 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: обзор команд

Библиотека 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