La Güeb de Joaquín
WPF
WPF - Commands
Sumario
Los comandos son un tipo especial de eventos. Son unos eventos abstractos y desacoplados de las interfaces de usuario. Reemplazan a los tradicionales eventos como el que se lanza cuando un usuario selecciona un elemento en una lista desplegable, y su característica más importante es que no están ligados a la interfaz de usuario que los expone.
Historial de este documento
Autor Joaquin Medina Serrano http://joaquin.medina.name/
Fechas importantes
· Creación.: sábado 15/Febrero/2007
· Modificación jueves, 29 de enero de 2015
· Impresión: jueves, 29 de enero de 2015/ 17:44
Palabras Clave
Commandos, Resources, command
1 Contenido
2.2 Cuatro conceptos básicos en los comandos WPF
2.3 Comunicación entre [VievModel] y [Vista]
2.4.2 Ejemplo Un Command sin parámetros
2.4.3 Ejemplo (1) Un Command Con parámetros
2.4.4 Ejemplo (2) Un Command Con parámetros
3.2 Uso de los Comandos desde XAML
Los comandos son un tipo especial de eventos. Son unos eventos abstractos y desacoplados de las interfaces de usuario. Reemplazan a los tradicionales eventos como el que se lanza cuando un usuario selecciona un elemento en una lista desplegable, y su característica más importante es que no están ligados a la interfaz de usuario que los expone.
Mientras los eventos están enlazados a acciones de usuario, los comandos representan acciones independientes de la interfaz de usuario.
(Algunos ejemplos de comandos son Cortar, Copiar y Pegar.)
Los comandos tienen varios propósitos.
· El primer propósito es separar la semántica y el objeto que invoca un comando de la lógica que lo ejecuta. Esto permite que varios orígenes dispares invoquen la misma lógica de comando y permite personalizar la lógica de comando para diferentes destinos. Por ejemplo, las operaciones de edición Copiar, Cortar y Pegar, que se encuentran en muchas aplicaciones, se pueden invocar mediante distintas acciones de usuario si se implementan con comandos. Una aplicación podría permitir a un usuario cortar texto u objetos seleccionados haciendo clic en un botón, eligiendo un elemento en un menú o utilizando una combinación de teclas, como CTRL+X. Mediante el uso de comandos, puede enlazar cada tipo de acción del usuario a la misma lógica.
· Otro propósito de los comandos es indicar si una acción está disponible. Para continuar el ejemplo de cortar un objeto o texto, la acción tiene sentido sólo cuando algo está seleccionado. Si un usuario intenta cortar un objeto o texto sin tener algo seleccionado, no pasaría nada. Para indicar esto al usuario, muchas aplicaciones deshabilitan los botones y elementos de menú para que sepa si es posible realizar una acción. Un comando puede indicar si una acción es posible implementando el método CanExecute. Un botón puede suscribirse al evento CanExecuteChanged y estar deshabilitado si CanExecute devuelve false o estar habilitado si CanExecute devuelve true.
· La semántica de un comando puede ser coherente entre aplicaciones y clases, pero la lógica de la acción es específica del objeto sobre el que se actúa. La combinación de teclas CTRL+X invoca el comando Cortar en clases de texto, clases de imagen y exploradores web, pero la lógica real para realizar la operación Cortar la define la aplicación que la realiza.
El modelo de comando se puede descomponer en cuatro conceptos básicos: el comando, el origen del comando, el destino del comando y el enlace del comando:
Por ejemplo: Supongamos un menú edición con la operación Pegar, cuyo objeto es pegar un texto en un control TextBox:
· [Commands] [El comando]. Es la acción que se va a ejecutar. (por ejemplo comando [Pegar]). El comando sabe en todo momento si la acción se puede realizar o no.
· [Commnad Sources] [El origen del comando]. Es el objeto que invoca el comando. Por ejemplo un Botón, o un MenuItem [MenuItem / edición / pegar (paste)].
· [Command Targets] [El destino del comando]. Es el objeto (un control es un objeto) donde se ejecuta el comando. Por ejemplo, el TextBox donde se pega el texto)
· [Command Bindings] [El enlace del comando]. Es el objeto que contiene toda la lógica para la operación a realizar. En el ejemplo que estoy poniendo, la toda la lógica necesaria para la operación ‘pegar’ está en el propio TextBox que recibe el texto pegado.
Una Observación: El objeto CommandBinding no siempre se suministra por el control (o la clase) de destino del comando. Con mucha frecuencia, el objeto CommandBinding debe ser creado por el desarrollador de la aplicación.
En el patrón MVVM, las comunicaciones entre la Vista y el Modelo_Vista se hacen mediante Comandos y Notificaciones de eventos:
La [Vista] comunica con el [Modelo_Vista] de dos formas:
· Con los enlaces de datos [Binding] ( que pueden ser bidireccionales y/o unidireccionales
· Llamando a los comandos que define el [Modelo_Vista]
Por su parte el [Modelo_Vista] se comunica con SU [Vista]
· Con los enlaces de datos [Binding] ( que pueden ser bidireccionales y/o unidireccionales
· Disparando eventos [PropetyChanged]
Concretamente las clases [Modelo_Vista]:
· Tienen que implementar la interfaz [InotifiPropetyChanged] para poder disparar el evento [PropetyChanged]. Esta implementación puede hacerse en cada una de las clases o bien crear una clase que encapsule toda la interfaz y que sea heredada por cada [Modelo_Vista] que tengamos en nuestra aplicación.
· Tiene que implementar Comandos para cada una de las acciones que necesite la [Vista]
· A efectos de claridad de código, es conveniente que las clases [Modelo_Vista] hereden todas de una clase base.
Los comandos de WPF se crean mediante la implementación de la interfaz ICommand. Es decir, A bajo nivel, un comando es cualquier objeto que implemente la interfaz ICommand, la cual define tres (3) miembros:
· Execute – Realiza las acciones que están asociadas al comando. En general, llama a un método que ejecuta la lógica del comando.
· CanExecute – Determina si el comando se puede ejecutar en el destino del comando actual. En general es un método que devuelve true, si el comando está habilitado, false si esta deshabilitado.
· CanExecuteChanged – Evento que se dispara si el valor de CanExecute cambia
Para crear un nuevo comando, solo debemos crear una nueva clase que implemente la interface ICommand (System.Windows.Input):
Public Class HelloCommand
Implements ICommand
Public Event CanExecuteChanged(sender As Object, e As EventArgs) _
Implements ICommand.CanExecuteChanged
Public Function CanExecute(parameter As Object) As Boolean _
Implements ICommand.CanExecute
RaiseEvent CanExecuteChanged(Me, EventArgs.Empty)
Return True
End Function
Public Sub Execute(parameter As Object) _
Implements ICommand.Execute
MessageBox.Show("Hola")
End Sub
End Class
Cuando se trabaja con el patrón MVVM hay que escribir comandos para comunicar la vista con el [Modelo Vista], con lo que aparece un montón de código repetido. Un tal [Josh Smith] ha diseñado una clase a la que ha llamado [RelayCommand] que implementa la interfaz ICommand y se usa desde el [Modelo Vista] como clase contenida y contiene toda la funcionalidad de un Command. Puedes ver la clase en C# en la siguiente dirección. https://gist.github.com/schuster-rainer/2648922
A continuación puedes ver la adaptación que he hecho de esa clase para Visual Basic. Y observaras que el nombre que le he dado es el de [DelegateCommand], porque he visto que es el nombre que le dan los programadores (en general) a esa clase con objeto de distinguirla de otros modelos que toman el nombre se [RelayCommand].
Esta clase encapsula toda la operatoria de la interfaz [ICommand] y se usa por la [VistaModelo] y por la [Vista]
'/**
'--------------------------------------------------
' Version .....: 2015/01/28
' [aaaa/mm/dd]
' Original en .:
'--------------------------------------------------
'
' Clase .....: CommandBase
'
' Lenguaje ..: Visual Basic .NET (2012)
' Autor .....: Joaquin (jms32) Medina Serrano; en {AGUILA-joaquin}
' -------------------------------------------------
'*/
'
'----------------------------------------------------------
Option Explicit On
Option Strict On
Option Infer Off
Option Compare Binary
'------------------------------------------------------------
'
Imports System.Windows.Input ' para [ICommand]
''' <summary>
''' Define un comando.
''' </summary>
''' <remarks>
''' <example>
''' <code>How to implement a reusable ICommand</code>
''' <code>http://www.wpftutorial.net/DelegateCommand.html</code>
'''
''' Compartiendo Código: Móvil + Web + Escritorio (2/2)
''' <code>http://geeks.ms/blogs/jyeray/archive/2010/11/14/compartiendo-c-243-digo-m-243-vil-web-escritorio-2-2.aspx</code>
''' </example>
'''
''' <example>
''' <code language="vb" title="Forma de usar la función NEW">
''' <![CDATA[
'''
''' ' Instancia de la clase que implementa [Icommand]
''' Private _saveCommand As DelegateCommand
'''
''' <summary>
''' Devuelve un [Comando] que guarda un cliente en el repositorio
''' </summary>
''' Public ReadOnly Property SaveCommand() As ICommand
''' Get
''' If _saveCommand Is Nothing Then
''' '-------------------------------
''' ' Los delegados de [Commnad]
''' ' El [Delegado] (la Accion) que llamara al metodo [Save]
''' Dim saveAction As Action(Of Object) = New Action(Of Object)(AddressOf Me.Save)
''' ' El [Delegado] que llamara al metodo [CanSave]
''' Dim canExecuteAction As Predicate(Of Object) = Function(param) Me.CanSave
''' '-------------------------------
''' ' Instanciar el Command ---> NEW <----
''' _saveCommand = New DelegateCommand(saveAction, canExecuteAction)
''' '
''' End If
''' Return _saveCommand
''' End Get
''' End Property
'''
'''
''' Public Sub Save(ByVal parameters As Object)
''' ' Proceso omitido para simplificar el codigo
''' End Sub
'''
''' Private ReadOnly Property CanSave() As Boolean
''' Get
''' ' Proceso de validacion del cliente omitido
''' Return True
''' End Get
''' End Property
'''
''' ]]>
''' </code>
''' </example>
''' </remarks>
Public Class DelegateCommand
Implements ICommand, IDisposable
''' <summary>
''' Encapsula un método que no tiene parámetros y no devuelve un valor
''' Es el método que se tiene que ejecutar [Execute]
''' </summary>
Protected _canExecuteAction As Predicate(Of Object) = Nothing
''' <summary>
''' Encapsula un método que devuelve un valor Lógico
''' Es el método que devuelve el valor [CanExecute]
''' </summary>
Protected _executeAction As Action(Of Object) = Nothing
''' <summary>
''' El nombre del comando (si se declara)
''' Solo a efectos informativos
''' </summary>
Protected _nombreCommand As String
''' <summary>
''' El nombre del comando (si se declara)
''' Solo a efectos informativos
''' </summary>
Public ReadOnly Property Name As String
Get
Return _nombreCommand
End Get
End Property
Public ReadOnly Property GetCommand As ICommand
Get
Return Me
End Get
End Property
#Region "Constructores"
''' <summary>
''' Constructor para las clases derivadas únicamente
''' </summary>
Protected Sub New()
'Me.New(String.Empty, Nothing, Nothing)
End Sub
''' <summary>
''' Constructor
''' </summary>
''' <param name="pexecute">
''' <para> <see cref="Action(Of Object)">Action(Of Object)</see></para>
''' <para> Encapsula un método que no tiene parámetros y no devuelve un valor</para>
''' <para> Es el método que se tiene que ejecutar [Execute]</para>
''' </param>
Public Sub New(ByVal pexecute As Action(Of Object))
Me.New(String.Empty, pexecute, Nothing)
End Sub
''' <summary>
''' Constructor
''' </summary>
''' <param name="pname">
''' <para> <see cref="String">String</see></para>
''' <para> El nombre del comando. A efectos informativos únicamente</para>
''' </param>
''' <param name="pexecute">
''' <para> <see cref="Action(Of Object)">Action(Of Object)</see></para>
''' <para> Encapsula un método que no tiene parámetros y no devuelve un valor</para>
''' <para> Es el método que se tiene que ejecutar [Execute]</para>
''' </param>
Public Sub New(ByVal pname As String, ByVal pexecute As Action(Of Object))
Me.New(pname, pexecute, Nothing)
End Sub
''' <summary>
''' Constructor
''' </summary>
''' <param name="pexecute">
''' <para> <see cref="Action(Of Object)">Action(Of Object)</see></para>
''' <para> Encapsula un método que no tiene parámetros y no devuelve un valor</para>
''' <para> Es el método que se tiene que ejecutar [Execute]</para>
''' </param>
''' <param name="pcanExecute">
''' <para> <see cref="Predicate(Of Object)">Predicate(Of Object)</see></para>
''' <para> Encapsula un método que devuelve un valor Lógico</para>
''' <para> Es el método que devuelve el valor [CanExecute]</para>
''' </param>
Public Sub New(ByVal pexecute As Action(Of Object), ByVal pcanExecute As Predicate(Of Object))
Me.New(String.Empty, pexecute, pcanExecute)
End Sub
''' <summary>
''' Constructor
''' </summary>
''' <param name="pname">
''' <para> <see cref="String">String</see></para>
''' <para> El nombre del comando. A efectos informativos únicamente</para>
''' </param>
''' <param name="pexecute">
''' <para> <see cref="Action(Of Object)">Action(Of Object)</see></para>
''' <para> Encapsula un método que no tiene parámetros y no devuelve un valor</para>
''' <para> Es el método que se tiene que ejecutar [Execute]</para>
''' </param>
''' <param name="pcanExecute">
''' <para> <see cref="Predicate(Of Object)">Predicate(Of Object)</see></para>
''' <para> Encapsula un método que devuelve un valor Lógico</para>
''' <para> Es el método que devuelve el valor [CanExecute]</para>
''' </param>
''' <remarks>
''' <example>
''' <para>Forma de usar la función NEW y parámetros que espera recibir </para>
'''
''' <code language="vb" title="Forma de usar la fucnion NEW">
''' <![CDATA[
'''
''' ' Instancia de la clase que implementa [Icommand]
''' Private _saveCommand As DelegateCommand
'''
''' <summary>
''' Devuelve un [Comando] que guarda un cliente en el repositorio
''' </summary>
''' Public ReadOnly Property SaveCommand() As ICommand
''' Get
''' If _saveCommand Is Nothing Then
''' '-------------------------------
''' ' Los delegados de [Commnad]
''' ' El [Delegado] (la Accion) que llamara al metodo [Save]
''' Dim saveAction As Action(Of Object) = New Action(Of Object)(AddressOf Me.Save)
''' ' El [Delegado] que llamara al metodo [CanSave]
''' Dim canExecuteAction As Predicate(Of Object) = Function(param) Me.CanSave
''' '-------------------------------
''' ' Instanciar el Command ---> NEW <----
''' _saveCommand = New DelegateCommand(saveAction, canExecuteAction)
''' '
''' End If
''' Return _saveCommand
''' End Get
''' End Property
'''
'''
''' Public Sub Save(ByVal parameters As Object)
''' ' Proceso omitido para simplificar el codigo
''' End Sub
'''
''' Private ReadOnly Property CanSave() As Boolean
''' Get
''' ' Proceso de validacion del cliente omitido
''' Return True
''' End Get
''' End Property
'''
''' ]]>
''' </code>
''' </example>
''' </remarks>
Public Sub New(ByVal pname As String, ByVal pexecute As Action(Of Object), ByVal pcanExecute As Predicate(Of Object))
MyBase.New()
Call CreateCommand(pname, pexecute, pcanExecute)
'Me._nombreCommand = pname
'Me._execute = pexecute
'Me._canExecute = pcanExecute
End Sub
''' <summary>
''' Carga los valores del Command.
''' Se usa por las clases derivadas
''' </summary>
''' <param name="pname">
''' <para> <see cref="String">String</see></para>
''' <para> El nombre del comando. A efectos informativos unicamente</para>
''' </param>
''' <param name="pexecute">
''' <para> <see cref="Action(Of Object)">Action(Of Object)</see></para>
''' <para> Encapsula un método que no tiene parámetros y no devuelve un valor</para>
''' <para> Es el método que se tiene que ejecutar [Execute]</para>
''' </param>
''' <param name="pcanExecute">
''' <para> <see cref="Predicate(Of Object)">Predicate(Of Object)</see></para>
''' <para> Encapsula un método que devuelve un valor Lógico</para>
''' <para> Es el método que devuelve el valor [CanExecute]</para>
''' </param>
''' <remarks>
''' </remarks>
Protected Sub CreateCommand(ByVal pname As String, ByVal pexecute As Action(Of Object), ByVal pcanExecute As Predicate(Of Object))
Me._nombreCommand = pname
Me._executeAction = pexecute
Me._canExecuteAction = pcanExecute
End Sub
#End Region
Private canExecuteValorAnterior As Boolean = False
''' <summary>
''' Define el método que determina si el comando se puede ejecutar en su estado actual.
''' </summary>
''' <param name="parameter">
''' <para>Tipo: <see cref="System.Object">System.Object</see> </para>
''' <para>Datos utilizados por el comando.</para>
''' <para>Si el comando no requiere datos para pasar, este objeto se puede
''' establecer en referencia null (Nothing en Visual Basic).</para>
''' </param>
''' <returns>
''' <para>Tipo: <see cref="System.Boolean">System.Boolean</see></para>
''' <para>true si este comando se puede ejecutar; si no, false.</para>
''' </returns>
''' <remarks>
''' <para>
''' Normalmente, un origen de comando llama al método de
''' CanExecute cuando se provoca el evento de CanExecuteChanged.
''' </para>
''' </remarks>
Public Function CanExecute(ByVal parameter As Object) As Boolean Implements System.Windows.Input.ICommand.CanExecute
Dim resultado As Boolean = False
If (_canExecuteAction Is Nothing) Then
resultado = True
Else
resultado = _canExecuteAction(parameter)
resultado = _canExecuteAction.Invoke(parameter)
End If
If canExecuteValorAnterior <> resultado Then
canExecuteValorAnterior = resultado
Call OnCanExecuteChanged()
End If
Return resultado
End Function
''' <summary>
''' Define el método que se llamará cuando se invoca el comando.
''' </summary>
''' <param name="parameter">
''' <para>Tipo: <see cref="System.Object">System.Object</see> </para>
''' <para>Datos utilizados por el comando.</para>
''' <para>Si el comando no requiere datos para pasar, este objeto se puede
''' establecer en referencia null (Nothing en Visual Basic).</para>
''' </param>
''' <remarks></remarks>
Public Sub Execute(ByVal parameter As Object) Implements System.Windows.Input.ICommand.Execute
If _executeAction IsNot Nothing Then
_executeAction(parameter)
End If
End Sub
''' <summary>
''' Se dispara cuando se producen cambios que afectan a si el comando se debe ejecutar.
''' </summary>
''' <remarks>
''' <para>
''' Normalmente, si no se puede ejecutar el comando, el origen de comando se deshabilita.</para>
'''</remarks>
Public Custom Event CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged
'Obligatorio. Los eventos declarados como Custom deben definir descriptores
'de acceso AddHandler, RemoveHandler y RaiseEvent personalizados.
AddHandler(ByVal value As EventHandler)
AddHandler CommandManager.RequerySuggested, value
End AddHandler
RemoveHandler(ByVal value As EventHandler)
RemoveHandler CommandManager.RequerySuggested, value
End RemoveHandler
RaiseEvent(ByVal sender As System.Object, ByVal e As System.EventArgs)
End RaiseEvent
End Event
''' <summary>
''' Dispara el evento [CanExecuteChanged]
''' </summary>
Public Sub OnCanExecuteChanged()
RaiseEvent CanExecuteChanged(Me, EventArgs.Empty)
End Sub
#Region "IDisposable Support"
Private _disposedValue As Boolean ' Para detectar llamadas redundantes
Protected Sub Dispose(disposing As Boolean)
If Not Me._disposedValue Then
If disposing Then
If Not (Me._executeAction Is Nothing) Then
Me._executeAction = Nothing
End If
If Not (Me._canExecuteAction Is Nothing) Then
Me._canExecuteAction = Nothing
End If
End If
Me._nombreCommand = Nothing
End If
Me._disposedValue = True
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
#End Region
End Class
Primero
Escribir el Command específico, por ejemplo un Command que muestre un mensaje al pulsar un botón
Public Class CommandHola
Inherits DelegateCommand
Implements IDisposable
Public Sub New()
MyBase.New()
'-------------------------------
' Los parámetros de [Commnad]
'-------------------------------
' El [Delegado] (la Accion) que llamara al método [Execute]
Dim localExcuteAction As Action(Of Object) = New Action(Of Object)(AddressOf Me.Execute)
' El [Delegado] que llamara al método [CanExecute]
Dim localCanExecuteAction As Predicate(Of Object) = Function(param) Me.CanExecute(param)
' El nombre de este command (A efectos informativos)
'Dim localNombreCommand As String = "Hola Command"
' Solo el nombre de la clase
Dim localNombreCommand As String = System.Reflection.MethodBase.GetCurrentMethod.DeclaringType.Name
'-------------------------------
' Pasar los valores a la clase base
'-------------------------------
MyBase._nombreCommand = localNombreCommand
MyBase._executeAction = localExcuteAction
MyBase._canExecuteAction = localCanExecuteAction
End Sub
Public Overloads Sub Execute(parameter As Object)
MessageBox.Show("Hola amigo !!! (Con invocación del comando)")
End Sub
Public Overloads Function CanExecute(parameter As Object) As Boolean
Return True
End Function
#Region "IDisposable Support"
Private _disposedValue As Boolean ' Para detectar llamadas redundantes
' IDisposable
Protected Overloads Sub Dispose(disposing As Boolean)
If Not Me._disposedValue Then
If disposing Then
MyBase.Dispose()
End If
End If
Me._disposedValue = True
End Sub
#End Region
End Class
Segundo
Declarar este Command en el apartado [Window.Resources]
<Window.Resources>
<local:CommandHola x:Key="CommandHolaResources" />
</Window.Resources>
Tercero
Enganchar el Command al botón
<Button x:Name="ButtonHola" Content="Hola"
HorizontalAlignment="Left" Margin="5"
VerticalAlignment="Top" Width="75"
Command="{Binding Mode=OneWay,
Source={StaticResource CommandHolaResources}}"/>
En el parámetro de un Commad se puede pasar cualquier cosa, en este caso vamos a pasar el objeto [TiempoModeloVista] que lo tenemos enlazado a los controles con un Binding
Primero
Escribir el command , que lo que hará es colocar en los cuadros de texto hora, minutos y segundos la hora actual, tomada del sistema. Observa que el Command trabaja con las propiedades del objeto [TiempoModeloVista] y son estas propiedades las que actualiza, pero como están enlazadas [Binding] con cuadros TextBox, pues los cambios en sus valores se muestran también en la ventana principal
Public Class CommandAhora
Inherits DelegateCommand
Implements IDisposable
Public Sub New()
MyBase.New()
'-------------------------------
' Los parámetros de [Commnad]
'-------------------------------
' El [Delegado] (la Accion) que llamara al método [Execute]
Dim localExcuteAction As Action(Of Object) = New Action(Of Object)(AddressOf Me.Execute)
' El [Delegado] que llamara al método [CanExecute]
Dim localCanExecuteAction As Predicate(Of Object) = Function(param) Me.CanExecute(param)
' El nombre de este command (A efectos informativos)
'Dim localNombreCommand As String = "Delete TextBox"
' solo el nombre de la clase
Dim localNombreCommand As String = System.Reflection.MethodBase.GetCurrentMethod.DeclaringType.Name
'-------------------------------
' Pasar los valores a la clase base
'-------------------------------
MyBase._nombreCommand = localNombreCommand
MyBase._executeAction = localExcuteAction
MyBase._canExecuteAction = localCanExecuteAction
End Sub
Public Overloads Sub Execute(parameter As Object)
' Recuperar el objeto pasado por parámetro
Dim objTiempo As TiempoModeloVista
objTiempo = TryCast(parameter, TiempoModeloVista)
Dim ahora As DateTime = DateTime.Now
objTiempo.Horas = ahora.Hour
objTiempo.Minutos = ahora.Minute
objTiempo.Segundos = ahora.Second
End Sub
Public Overloads Function CanExecute(parameter As Object) As Boolean
Return True
End Function
#Region "IDisposable Support"
Private _disposedValue As Boolean ' Para detectar llamadas redundantes
' IDisposable
Protected Overloads Sub Dispose(disposing As Boolean)
If Not Me._disposedValue Then
If disposing Then
MyBase.Dispose()
End If
End If
Me._disposedValue = True
End Sub
#End Region
End Class
Segundo
Declarar este Command en el apartado [Window.Resources]
<Window.Resources>
<local:CommandAhora x:Key="CommandAhoraResources" />
</Window.Resources>
Tercero
Enganchar el Command al botón
<Button x:Name="ButtonAhora" Content="Ahora"
HorizontalAlignment="Left" Margin="5"
VerticalAlignment="Top" Width="75"
Command="{Binding Mode=OneWay,
Source={StaticResource CommandAhoraResources}}"
CommandParameter="{Binding Mode=OneWay,
Source={StaticResource TiempoModeloVistaResources}}"
CommandTarget="{Binding Mode=OneWay,
Source={StaticResource TiempoModeloVistaResources}}"/>
Ejemplo, queremos escribir un Command que borre un cuadro de texto. Evidentemente, el command estará enlazado con (por ejemplo) un botón y cuando se pulse se borrara el cuadro de texto. En este caso el valor que se recibe por parámetro en un control [TextBox]
Primero el Command
''' <summary>
''' Borra el contenido de un TextBox
''' </summary>
Public NotInheritable Class TextBoxDeleteCommand
Inherits DelegateCommand
Implements IDisposable
Public Sub New()
MyBase.New()
'-------------------------------
' Los parámetros de [Commnad]
' El [Delegado] (la Accion) que llamara al metodo [cmdExecute]
Dim localExcuteAction As Action(Of Object) = New Action(Of Object)(AddressOf Me.cmdExecute)
' El [Delegado] que llamara al metodo [cmdCanExecute]
Dim localCanExecuteAction As Predicate(Of Object) = Function(param) Me.cmdCanExecute(param)
' el nombre de este command
Dim localNombreCommand As String = "Delete TextBox"
'-------------------------------
MyBase._nombreCommand = localNombreCommand
MyBase._execute = localExcuteAction
MyBase._canExecute = localCanExecuteAction
End Sub
''' <summary>
''' Esta función Borra todo el texto de un textBox pasado por parámetro
''' </summary>
Private Sub cmdExecute(ByVal parameter As Object)
If parameter IsNot Nothing Then
Dim target As TextBox = TryCast(parameter, TextBox)
If target IsNot Nothing Then
If target.Text.Length > 0 Then
target.Clear()
End If
End If
End If
End Sub
''' <summary>
''' Esta función Comprueba que en TextBox haya un texto para poder ejecutar la función [Execute]
''' Si hay un texto devuelve (True) [Si se puede ejecutar], si no hay texto devuelve false [No se puede ejecutar]
''' </summary>
Private ReadOnly Property cmdCanExecute(ByVal parameter As Object) As Boolean
Get
Dim resultado As Boolean = False
If parameter IsNot Nothing Then
Dim target As TextBox = TryCast(parameter, TextBox)
If target IsNot Nothing Then
If target.Text.Length > 0 Then
resultado = True
End If
End If
End If
Return resultado
End Get
End Property
#Region "IDisposable Support"
Private _disposedValue As Boolean ' Para detectar llamadas redundantes
' IDisposable
Protected Overloads Sub Dispose(disposing As Boolean)
If Not Me._disposedValue Then
If disposing Then
MyBase.Dispose()
End If
' liberar recursos no administrados (objetos no administrados) e invalidar Finalize() below.
' Establecer campos grandes como Null.
End If
Me._disposedValue = True
End Sub
#End Region
End Class
Segundo, Declararlo en Window Resources de la Vista
<UserControl.Resources>
<!--el comando [Borrar un TetxBox]-->
<local:TextBoxDeleteCommand x:Key="TextBoxDeleteCommandResources" />
</UserControl.Resources>
Tercero el Botón
El TextBox que vamos a borrar
<TextBox Grid.Column="0" x:Name="TextBoxInterno" />
<Button x:Name="ButtonBorrarUri"
Grid.Column="1"
HorizontalAlignment="Right" VerticalAlignment="Top" Width="20"
Command="{Binding Mode=OneWay,
Source={StaticResource TextBoxDeleteCommandResources}}"
CommandParameter="{Binding ElementName=TextBoxInterno,
Mode=OneWay}"
CommandTarget="{Binding ElementName=TextBoxInterno, Mode=OneWay}">
<Button.Content>
<Image Source="action_Cancel_16xLG_BN.png"
Stretch="None" />
</Button.Content>
</Button>
La implementación de WPF de ICommand es la clase RoutedCommand. Los principales orígenes de entrada en WPF son el mouse, el teclado, la entrada de lápiz y los comandos enrutados. Las entradas más orientadas a dispositivo utilizan un objeto RoutedEvent para notificar a los objetos de una página de aplicación que se ha producido un evento de entrada. Un RoutedCommand no es diferente.
Los métodos Execute y CanExecute de un objeto RoutedCommand no contienen la lógica de aplicación para el comando, sino que provocan eventos enrutados que se tunelizan y se propagar a través del elemento hasta que encuentran un objeto con CommandBinding.
El objeto CommandBinding contiene los controladores para estos eventos y son los controladores los que realizan el comando. Para obtener más información sobre el enrutamiento de eventos en WPF, vea MSDN - Información general sobre eventos enrutados.
El método Execute de un objeto RoutedCommand provoca los eventos PreviewExecuted y Executed en el destino del comando. El método CanExecute de un objeto RoutedCommand provoca los eventos CanExecute y PreviewCanExecute en el destino del comando. Estos eventos se tunelizan y se propagan a través del árbol de elementos hasta que encuentran un objeto que tiene un objeto CommandBinding para ese comando en particular.
WPF proporciona un conjunto de comandos enrutados comunes y expuestos como propiedades estáticas en cinco clases diferentes: MediaCommands, ApplicationCommands, NavigationCommands, ComponentCommands y EditingCommands. Estas clases constan solamente de los objetos RoutedCommand y no de la lógica de implementación del comando. La lógica de implementación es responsabilidad del objeto en el que se ejecuta el comando.
Close, Copy, Cut, Delete, Find, Help, New, Open, Paste, Print, PrintPreview, Properties, Redo, Replace, Save, SaveAs, SelectAll, Stop, Undo, etc.
MoveDown, MoveLeft, MoveRight, MoveUp, ScrollByLine, ScrollPageDown, ScrollPageLeft, ScrollPageRight, ScrollPageUp, SelectToEnd, SelectToHome, SelectToPageDown, SelectToPageUp, etc.
ChannelDown, ChannelUp, DecreaseVolume, FastForward, IncreaseVolume, MuteVolume, NextTrack, Pause, Play, PreviousTrack, Record, Rewind, Select, Stop, etc.
BrowseBack, BrowseForward, BrowseHome, BrowseStop, Favorites, FirstPage, GoToPage, LastPage, NextPage, PreviousPage, Refresh, Search, Zoom, etc.
AlignCenter, AlignJustify, AlignLeft, AlignRight, CorrectSpellingError, DecreaseFontSize, DecreaseIndentation, EnterLineBreak, EnterParagraphBreak, IgnoreSpellingError, IncreaseFontSize, IncreaseIndentation, MoveDownByLine, MoveDownByPage, MoveDownByParagraph, MoveLeftByCharacter, MoveLeftByWord, MoveRightByCharacter, MoveRightByWord, etc.
Existen controles que incluyen y exponen la lógica de comandos por lo que no debemos de preocuparnos de realizar ningún trabajo sobre comandos. Estos controles implementan el interfaz ICommandSource exponiendo los siguientes miembros:
· Command: Se le asigna el comando a ejecutar.
· CommandParameter: Representa un valor definido por el usuario que será pasado como parámetro al comando.
· CommandTarget: Objeto al que afecta la ejecución del comando.
En el siguiente ejemplo veremos cómo usar los comandos preestablecidos de Cortar, Pegar y Copiar que afectarán a una caja de texto. Estos comandos serán lanzados por varios botones de la barra de herramientas.
Utilizando DataBinding asignamos como CommandTarget el elemento de tipo TextBox y de nombre txtDocument. En Command irá cada uno de los comandos a asignar: Cut, Copy o Paste.
<StackPanel>
<ToolBar>
<Button Command="Cut"
CommandTarget="{Binding ElementName=txtDocument}">Cortar</Button>
<Button Command="Copy"
CommandTarget="{Binding ElementName=txtDocument}">Copiar</Button>
<Button Command="Paste"
CommandTarget="{Binding ElementName=txtDocument}">Pegar</Button>
</ToolBar>
<TextBox Name="txtDocument" Width="300"></TextBox>
</StackPanel>
· CommandBinding (Clase)
· http://msdn.microsoft.com/es-es/library/vstudio/system.windows.input.commandbinding.aspx
· RoutedCommand (Clase)
· http://msdn.microsoft.com/es-es/library/vstudio/system.windows.input.routedcommand.aspx
· Wpf - La interfaz [INotifyPropertyChanged] http://joaquin.medina.name/web2008/documentos/informatica/lenguajes/puntoNET/System/Windows/WpfDocs/Docs/WPF_ImplementacionINotifyPropertyChanged.html
· How to implement a reusable ICommand
· http://www.wpftutorial.net/DelegateCommand.html
· Compartiendo Código: Móvil + Web + Escritorio (2/2)
· Blog de Oskar Alvarez Commands en WPF
· http://geeks.ms/blogs/oalvarez/archive/2009/07/10/commands-en-wpf.aspx
· Implementation from Josh Smith of the RelayCommand
· https://gist.github.com/schuster-rainer/2648922
· A Practical Quick-start Tutorial on MVVM in WPF
· http://www.codeproject.com/Articles/81484/A-Practical-Quick-start-Tutorial-on-MVVM-in-WPF
· DelegateCommand in VB.NET
· http://deepxpress.blogspot.com.es/2011/04/delegatecommand-in-vbnet.html
· Descripción de eventos y comandos dirigidos en WPF
· http://msdn.microsoft.com/es-es/magazine/cc785480.aspx