Ejemplo de patrón de comandos WPF personalizado


He hecho algo de programación WPF y una cosa que nunca obtuve fue el patrón de comandos. Cada ejemplo parece ser para construido en unos, editar, cortar, pegar. ¿Alguien tiene un ejemplo o sugerencia de mejores prácticas para comandos personalizados?

Author: sig, 2008-08-12

4 answers

Ah ha! Una pregunta que puedo responder! En primer lugar, debo mencionar que personalmente he encontrado más fácil definir y conectar comandos en código en lugar de en XAML. Me permite conectar los controladores para los comandos un poco más flexible que un enfoque all XAML.

Debes averiguar qué comandos quieres tener y con qué se relacionan. En mi aplicación, actualmente tengo una clase para definir comandos de aplicación importantes como este:

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));
    // ...
  }

Ahora, porque quería para mantener el código todo junto, usando un enfoque de solo código para los comandos me permite poner los siguientes métodos en la clase anterior:

/// 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;
}

El segundo método puede incluso ser compartido con otros Comandos sin tener que repetirlo por todo el lugar.

Una vez que haya definido los comandos como este, puede agregarlos a cualquier parte de la interfaz de usuario. En lo siguiente, una vez que la ventana se ha cargado, agrego enlaces de comandos tanto a la Ventana como al MenuItem y luego agrego un enlace de entrada a la Ventana usando un bucle para hacer esto para todos los enlaces de comandos. El parámetro que se pasa es la ventana de su auto por lo que el código de arriba sabe qué Ventana para tratar de cerrar.

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;
    // ...
  }

  // ....
}

Luego también tengo controladores de eventos para los eventos windowClosing y WindowClosed, le recomiendo que haga que la implementación real de los comandos sea lo más pequeña y genérica posible. Como en este caso, no traté de poner código que intenta detener el cierre de la ventana si hay datos no guardados, mantuve ese código firmemente dentro del WindowClosing evento.

Hágame saber si tiene alguna pregunta de seguimiento. :)

 38
Author: Nidonocu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-03-03 09:45:23

Escribí un blog sobre un montón de recursos en comandos WPF junto con un ejemplo el año pasado en http://blogs.vertigo.com/personal/alanl/Blog/archive/2007/05/31/commands-in-wpf.aspx

Pegando aquí:

Ejemplo del capítulo de Adam Nathan sobre Nuevos Conceptos Importantes en WPF: Commands

Artículo de MSDN: El Patrón De Comandos En WPF

Keyvan Nayyeri: Cómo agregar Comandos al Control WPF Personalizado

Ian Griffiths: Avalon Input, Comandos y Manejadores

Wikipedia: Command Pattern

Biblioteca MSDN: Descripción general

Biblioteca MSDN: CommandBinding Class

Biblioteca MSDN: Temas prácticos de Entrada y Comandos

Biblioteca MSDN: EditingCommands Class

Biblioteca MSDN: MediaCommands Class

Biblioteca MSDN: ApplicationCommands Class

Biblioteca MSDN: NavigationCommands Clase

Biblioteca MSDN: ComponentCommands Class

También enterrado en las muestras del SDK de WPF, hay una buena muestra en la edición de RichTextBox que he ampliado. Puede encontrarlo aquí: RichTextEditor.zip

 7
Author: Alan Le,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-29 21:30:11

En la edición de septiembre de 2008 de la revista MSDN, Brian Noyes tiene un excelente artículo sobre RoutedCommand/RoutedEvents!!!

Aquí está el enlace: http://msdn.microsoft.com/en-us/magazine/cc785480.aspx

 4
Author: rudigrobler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-08 07:42:11

Lo que pasa con XAML es que está bien para programas 'simples', pero tristemente, no funciona bien cuando quieres hacer cosas como compartir funciones. Digamos que tienes varias clases y UI todas las cuales tenían comandos que nunca fueron deshabilitados, tendrías que escribir un método 'CanAlwaysExecute' para cada Ventana o UserControl! Eso no es muy SECO.

Habiendo leído varios blogs y probando varias cosas, he tomado la decisión de hacer XAML puramente sobre looks, estilos, animación y disparadores. Toda mi conexión con los controladores de eventos y el mando ahora está en el código detrás. :)

Otro gotcha por cierto es el enlace de entrada, para que se capten, el foco debe estar en el objeto que contiene los enlaces de entrada. Por ejemplo, para tener un atajo que puedes usar en cualquier momento (por ejemplo, F1 para abrir ayuda), ese enlace de entrada debe estar configurado en el objeto Window, ya que siempre tiene foco cuando tu app está activa. El uso del método de código debería hacer que sea más fácil, incluso cuando comience a usar UserControls que podrían querer agregar enlaces de entrada a su ventana padre.

 3
Author: Nidonocu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-12 07:57:11