Comandos

El objetivo de los comandos es enlazar funcionalidad a la interfaz de usuario.

Por ejemplo si necesitamos agregar nuevas personas a una lista. Una primera solución sería añadir un botón donde al evento click le añadiríamos el código necesario para poder agregar nuevas personas a una lista.

private void BtnAgregar_Click(object sender, RoutedEventArgs e) {
  var datos =LayoutRoot.DataContext as Datos;
  datos.Personas.Add (new Persona() { Nombre = Guid.NewGuid().ToString(), Pais="España", Edad=20});
}

Para reutilizar esta funcionalidad tendríamos que repetir el código.  Veamos como se mejoraría la funcionalidad mediante un comando.

Itenrfaz ICommand

Los comandos son objetos que implementan la interfaz ICommand. Estos objetos pueden ser enlazados a la propiedad Command que tienen los controles de tipo ButtonBase e HyperLink.

La interfaz ICommand pertenece al espacio de nombres System.Windows.Input y tiene tres miembros que hay que implementar:

  1. Execute: establece lo que el comando hará, es decir donde se encuentra la funcionalidad del comando.
  2. CanExecute: devuelve un boolean e indica si se puede o no ejecutar el comando.
  3. CanExecuteChanged: al dispararse evalúa si el comando puede o no ejecutarse, es decir ejecutaría el método CanExecute.

Creando comandos

Veamos un ejemplo:

public class AgregarPersonaCommand : ICommand {
  public bool CanExecute (object parameter) {
    throw new NotImplementedException(); }

  public event eventHandler CanExecuteChanged;

  public void Execute (object parameter) {
    throw new NotImplementedException();
  }
}

Ahora hay que solicitar un delegado en el constructor de la clase y será el delegado el que ejecutemos en el método execute. En este caso será un delegado “action” ya que no necesitamos argumentos adicionales en este momento.

public class AgregarPersonaCommand : ICommand {
  Action action;
  public AgregarPersonaCommand (Action action) {
    this.action = action;
  }

  public bool CanExecute (object parameter) {
    return true; // Se ejecutará siempre.
  }

  public event eventHandler CanExecuteChanged;

  public void Execute (object parameter) {
    action(); // La acción que pasamos en el constructor
  }
}

Ahora sólo falta exponerlo dentro de la clase que va a ser usado, es decir, la clase Datos.

<UserControl.Resources>
  <local:Datos x:Key="datos" />
</UserControl.Resources>
...
<Grid x:Name="LayoutRoot" Background="White" DataContext="{Binding Source={StaticResource datos}}">
  // Clase Datos
  public AgregarPersonaCommand AgregarPersona {get; private set;} // Sólo lectura pública

Ahora hay que inicializar el comendo dentro de la clase:

  public Datos() {
    AgregarPersona = new AgregarPersonaCommand(Agregar); }

  void Agregar() {
    Personas.Add(new Persona() {
      Nombre = Guid.newGuid().Tostring(), Pais = "España", Edad=33});
  }

Ahora la funcionalidad del BtnAgregar_Click que hicimos al principio ya no es necesaria.

Los comandos los podemos enlazar a la propiedad Command que toda la familia de controles ButtonBase e Hyperlink; por lo que mediante código XAML del botón de nuestra aplicación podría quedar de la siguiente manera:

 <Button Content="Agregar" Height="23" Width="75" Command="{Binding AgregarPersona}" />

De momento los datos se están creando con datos fijos. Los comandos soportan parámetros a través de CommandParameter que también están en toda la familia ButtonBase e Hyperlink.

Parámetros de comandos

<Button Content="Agregar" Height="23" Width="75" Command="{Binding AgregarPersona}" 
  CommandParamenter ="{Binding Text, ElementName=txtNombre}" />

Ahora hay que modificar el delegado Action, por lo que tendríamos que modificar el comando para solicitar el Action genérico, específicamente Action.

public class AgregarPersonaCommand : ICommand {
  Action<string> action;
  public AgregarPersonaCommand (Action<string> action) {
    this.action = action;
  }

  public bool CanExecute (object parameter) {
    return true; // Se ejecutará siempre.
  }

  public event eventHandler CanExecuteChanged;

  public void Execute (object parameter) {
    action(parameter.ToString()); // La acción que pasamos en el constructor
  }
}

Ahora también hay que Agregar en la clase Datos también deberá ser modificado para cumplir con la signatura del delegado Action.

  
  void Agregar() {
    Personas.Add(new Persona() {
      Nombre = nombre, Pais = "España", Edad=33});
  }

Ejecución condicional de comandos

El comando se ejecutará siempre (CanExecute=true;), si queremos se ejecute cuando el nombre no sea nulo o cadena vacía:

public class AgregarPersonaCommand : ICommand {
  Action<string> action;
  public AgregarPersonaCommand (Action<string> action) {
    this.action = action;
  }

  public bool CanExecute (object parameter) {
    return parameter != null ?
      !string.IsNullOrempty(parameter.ToString()):false;
  }

  public event eventHandler CanExecuteChanged;

  public void Execute (object parameter) {
    action(parameter.ToString()); // La acción que pasamos en el constructor
  }
}

Vamos a incluir un segundo constructor en la clase AgregarPersonaCommand para solicitar una función de tipo Func.

public class AgregarPersonaCommand : ICommand {
  Action <string> action;
  Func<bool> canExecute;

  public AgregarPersonaCommand (Action<string> action) :
    this(action, () => true) { }

  public AgregarPersonaCommand (Action<string> action, Func<bool> canExecute) {
    this.action = action;
    this.canExecute = canExecute; }

  public bool CanExecute (object parameter) {
    bool tieneValor = parameter != null ? 
      !string.IsNullOrEmpty(parameter.ToString()) : false;
    return tieneValor && this.canExecute();
  }

  public event eventHandler CanExecuteChanged;

  public void Execute (object parameter) {
    action(parameter.ToString()); // La acción que pasamos en el constructor
  }
}

Es responsabilidad únicamente del parámetro CanExecute el determinar si se puede ejecutar el comando o no.

Del lado de la clase Datos, cuando instanciemos AgregarCommand podríamos pasarle la función que determine si puede ejecutar el comando, en este caso, si el número de objetos en la colección Personas en máximo 10.

 

  AgregarPersona = new AgregarPersonaCommand(Agregar, ()=>Personas.Count<10);

 

Todavía no se cumpliría esta condición necesitamos avisar al comando que requiere reevaluar el método Canexecute, cada vez que hemos agregado una nueva persona a la colección. Esto se hará con CanExecuteChanged.

public void OnCanExecuteChanged() { 
  if (CanexecuteChanged != null) {
    CanexecuteChanged(this, null);
  }

este nuevo método lo invocaremos cuando la colección haya sido modificada:

public Datos() {
  AgregarPersona = new AgregarPersonaCommand (Agregar, ()=>Personas.Count<10);   Personas.CollectionChanged += (s,a) => AgregarPersona.OnCanexecuteChanged();

Pasando parámetros complejos a los comandos

hasta este punto sólo estamos pasando el nombre de la persona que será creada, pero si queremos pasar el pais y la edad, ya que sólo podemos pasar uno a la vez. Entonces nos tocará pasarlo todo en un objeto llamado “persona” como valor de CommandParameter.

...
<TextBox width="150" Margin="5" Text="{Binding Path=Nombre, Mode=TwoWay}" />
<TextBox width="150" Margin="5" Text="{Binding Path=Pais, Mode=TwoWay}" />
<TextBox width="150" Margin="5" Text="{Binding Path=Edad, Mode=TwoWay}" />
...
<Button Width="100" Margin="100" Content="Agregar" CommandParameter="{Binding DataContext, ElementName=panelNuevaPersona}" Command="{Binding Path=AgregarPersona}" />
...

La clase AgregarPersonaCommand:

public class AgregarPersonaCommand : ICommand {
  Action<Persona> action;
  Func canExecute;

  public AgregarPersonaCommand (Action<Persona> action) :
    this(action, () => true) { }

  public AgregarPersonaCommand (Action<Persona> action, Func<bool> canExecute) {
    this.action = action;
    this.canExecute = canExecute; }

  public bool CanExecute (object parameter) {
    bool tieneValor = parameter != null ? 
      !string.IsNullOrEmpty(parameter.ToString()) : false;
    return tieneValor && this.canExecute();
  }

  public event eventHandler CanExecuteChanged;

  public void Execute (object parameter) {
    action(parameter.ToString()); // La acción que pasamos en el constructor
  }
}

Método Agregar:

void Agregar (Persona persona) {
  Personas.Add(new Persona() {
    Nombre = persona.Nombre;
    Edad = persona.Edad;
    Pais = persona.Pais;
  });
}

2 comentarios en “Comandos

  • el 23 febrero, 2017 a las 10:20 pm
    Permalink

    Excelente información!, simple y super util

    Respuesta
  • el 11 marzo, 2018 a las 7:15 pm
    Permalink

    Gracias. Me sirvió para iniciarme con los comandos. Para mi siempre fue un lio entenderlos

    Respuesta

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.