' /** ' -------------------------------------------------- ' [- Versión -] = 23.10.17.2023 ' [- Class Name -] = DelegateCommand ' [- Descripción -] = ' Clase base para usar los comandos. ' Define un comando. ' --------------- ' [- Module Name -] = DelegateCommand.vb ' [- Type -] = Visual Basic Source file ' [- Size -] = 19,06 KB -> - {19513} Bytes- ' --------------- ' [- Versión .NET Framework -] = 4.0.30319.42000 ' [- Versión .NET año -] = 2016 ' [- Propietario -] = AGUILA10\Joaquin ' --------------- ' [- Observaciones -] = ' Si está utilizando el patrón MVVM (Model-View-ViewModel), uno de los ' mecanismos más utilizados para unir las acciones a la vista son los ' comandos. ' Para proporcionar un comando, tiene que implementar la interfaz ICommand. ' Este patrón tiene dos delegados: ' uno que se llama, cuando se invoca ICommand. ' Execute (param objeto) ' y otro que evalúa el estado del Command cuando se llama a ICommand. ' CanExecute (param objeto) ' --------------- ' [- Autor: Nombre -] = Joaquin (jms32) Medina Serrano; en {AGUILA-Joaquin} ' [- Autor: CorreoE -] = mailto:administrador@joaquin.medina.name ' --------------- ' [- Código de ejemplo -] = ' '' ------------------------------------------------------------------ ' '' Ejemplo (UNO) de uso de un command como clase derivada ' '' ------------------------------------------------------------------ ' ' ' Instancia de la clase que implementa [Icommand] ' Private _saveCommand As DelegateCommand ' ' ' Devuelve un [Comando] que guarda un cliente en el repositorio ' ' Public ReadOnly Property SaveCommand() As ICommand ' Get ' If _saveCommand Is Nothing Then ' '------------------------------- ' ' Los delegados de [Command] ' ' El [Delegado] (la Acción) que llamara al método [Save] ' Dim saveAction As Action(Of Object) = New Action(Of Object)(AddressOf Me.Save) ' ' El [Delegado] que llamara al método [CanSave] ' Dim canExecuteAction As Predicate(Of Object) = Function(param) Me.CanSave ' '------------------------------- ' ' Instanciar el Command ' _saveCommand = New DelegateCommand(saveAction, canExecuteAction) ' ' Juego de teclas que activan el comando (P.e: Ctrl + H) ' _localDelegateCommand.KeyBinding = New KeyBinding(_localDelegateCommand, Key.H, ModifierKeys.Control) ' End If ' Return _saveCommand ' End Get ' End Property ' ' ' Guarda (por ejemplo, un cliente en el repositorio). ' Este método es invocado por [SaveCommand]. ' ' Public Sub Save(ByVal parameters As Object) ' ' Proceso omitido para simplificar el código ' End Sub ' ' ' Por ejemplo Devuelve True si el cliente es válido y puede guardarse ' Este método es invocado por [SaveCommand]. ' ' Private ReadOnly Property CanSave() As Boolean ' Get ' ' Proceso de validación del cliente omitido ' ' Return String.IsNullOrEmpty(Me.ValidateCustomerType()) AndAlso _customer.IsValid ' Return True ' End Get ' End Property ' ' ' ' '#Region " Command [BotonCargaDirectorioCommand] " ' ' ' ' ''' ' ' ''' Constructor de la clase e instancia del command ' ' ''' ' ' Public Sub New() ' ' ' ' ' crear e instanciar los commands ' ' Call CreateBotonCargaDirectorioCommand() ' ' '------------------------------- ' ' End Sub ' ' ' ' '' ------------------------------------------------------------------ ' '' Ejemplo (DOS) de uso de un command en una clase Modelo Vista ' '' de esta forma definimos el command dentro de la clase ' '' y Can Execute como una propiedad ' '' ------------------------------------------------------------------ ' ''' ' ''' Un command para el botón aceptar ' ''' Tiene que ser una propiedad para poder enlazarlo con un binding ' ''' ' Public Property BotonCargaDirectorioCommand As DelegateCommand ' ' ''' ' ''' Instancia del command. Tiene que ser llamada desde NEW ' ''' ' Private Sub CreateBotonCargaDirectorioCommand() ' '------------------------------- ' ' Los parámetros de [Command] ' ' El [Delegado] (la Acción) que llamara al método [cmdExecute] ' Dim localExcuteAction As Action(Of Object) = New Action(Of Object)(AddressOf Me.BotonAceptarCmdExecute) ' ' El [Delegado] que llamara al método [cmdCanExecute] ' Dim localCanExecuteAction As Predicate(Of Object) = Function(param) Me.BotonAceptarCmdCanExecute(param) ' '------------------------------- ' ' instanciar el command ' BotonCargaDirectorioCommand = New DelegateCommand(localExcuteAction, localCanExecuteAction) ' '--------------------------- ' ' Valores opcionales y descriptivos ' BotonCargaDirectorioCommand.ComandoNombre = "BotonCargaDirectorioCommand" ' BotonCargaDirectorioCommand.ComandoDescripcion = "proceso a seguir cuando se pulsa el botón carga directorios" ' End Sub ' ' ' ''' ' ''' Proceso que realiza el Command ' ''' ' ''' ' Public Sub BotonAceptarCmdExecute(ByVal parameter As Object) ' ' por ejemplo copiar algo en algún sitio ' objColeccionFotos.Path = Me.TextBoxDirectorio ' End Sub ' ' ''' ' ''' Esta función Comprueba que (por ejemplo, en un TextBox hay un texto ) si se puede ejecutar la función [Execute] ' ''' Si se puede ejecutar, devuelve [True] , en caso contrario devuelve [False], No se puede ejecutar ' ''' ' Private ReadOnly Property BotonAceptarCmdCanExecute(ByVal parameter As Object) As Boolean ' Get ' ' comprobación de si se puede ejecutar el command ' Return True ' End Get ' End Property ' '#End Region ' ' ' '' ------------------------------------------------------------------ ' '' ------------------------------------------------------------------ ' ' --------------- ' [- Fecha de creación -] = 2015/12/31T23:26:41Z ' [- Último acceso -] = 2016/01/31T13:26:08Z ' [- Última modificación -] = 2023/10/17 T 09:56:00 Z ' --------------- ' [- Historial de revisiones -] = ' - 2013/03/24 - Creación de la clase ' - 2013/12/24 - Añado eventos {Inicio}, y {Fin} del proceso ' - - http://www.codeproject.com/Tips/695219/A-Generic-Implementation-for-ICommand ' - - Modifico {Execute} para que dispare los eventos ' - 2014/02/05 - Modifico los eventos ' - 2016/01/31 - documento esta clase ' - 2017/02/27 - Añado los constructores públicos, y el ejemplo dos para mostrar como se usa en una clase modelo vista ' - 2023/10/17 - Repaso, complemento y documento esta clase, cambio el acceso [ Protected Friend] ' a los eventos [OnCanExecuteChanged], y [OnExecuteStart], y [OnExecuteCompleted] ' --------------- ' [- Información del Generador -] ' - Nombre - WpfDocumentadorClases2016 ' - Version - 2.2.5.1 ' -------------------------------------------------- ' [- /Eof -] ' */ ' '---------------------------------------------------------- Option Explicit On Option Strict On Option Infer Off Option Compare Binary '------------------------------------------------------------ ' Namespace Util.Command ''' ''' Define un comando. ''' ''' ''' ''' How to implement a reusable ICommand ''' http://www.wpftutorial.net/DelegateCommand.html ''' ''' Compartiendo Código: Móvil + Web + Escritorio (2/2) ''' http://geeks.ms/blogs/jyeray/archive/2010/11/14/compartiendo-c-243-digo-m-243-vil-web-escritorio-2-2.aspx ''' ''' ''' ''' ''' ''' Devuelve un [Comando] que guarda un cliente en el repositorio ''' ''' ''' 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 ''' _saveCommand = New DelegateCommand(saveAction, canExecuteAction) ''' ' Juego de teclas que activan el comando (P.e: Ctrl + H) ''' _localDelegateCommand.KeyBinding = New KeyBinding(_localDelegateCommand, Key.H, ModifierKeys.Control) ''' End If ''' Return _saveCommand ''' End Get ''' End Property ''' ''' ''' ''' ''' Guarda (por ejemplo, un cliente en el repositorio). ''' Este metodo es invocado por [SaveCommand]. ''' ''' ''' Public Sub Save(ByVal parameters As Object) ''' ' Proceso omitido para simplificar el codigo ''' End Sub ''' ''' ''' ''' Por ejemplo Devuelve True si el cliente es valido y puede guardaarse ''' Este metodo es invocado por [SaveCommand]. ''' ''' ''' Private ReadOnly Property CanSave() As Boolean ''' Get ''' ' Proceso de validacion del cliente omitido ''' ' Return String.IsNullOrEmpty(Me.ValidateCustomerType()) AndAlso _customer.IsValid ''' Return True ''' End Get ''' End Property ''' ''' ]]> ''' ''' ''' Public Class DelegateCommand Implements System.Windows.Input.ICommand Implements System.IDisposable #Region "Variables y propiedades de la clase" ''' ''' Encapsula un método que no tiene parámetros y no devuelve un valor ''' Es el método que se tiene que ejecutar [Execute] ''' Protected _canExecuteAction As Predicate(Of Object) = Nothing 'Protected _canExecuteAction As Func(Of Object, Boolean) = Nothing ''' ''' Encapsula un método que devuelve un valor Lógico ''' Es el método que devuelve el valor [CanExecute] ''' Protected _executeAction As Action(Of Object) = Nothing #Region "Helpers" ''' ''' [Opcional][Informativo] El nombre del comando ''' Si se declara, Hay que cargarlas específicamente a través de la propiedad ''' Public Property ComandoNombre As String = String.Empty ''' ''' [Opcional][Informativo] Descripción de lo que hace este command. ''' Si se declara, Hay que cargarlas específicamente a través de la propiedad ''' ''' ''' Una cadena que describe lo que hace el command ''' Valor por defecto [String.Empty] ''' Public Property ComandoDescripcion As String = String.Empty ''' ''' [Opcional] Juego de teclas que activan el comando (P.e: Ctrl + V) ''' Si se declara, Hay que cargarlas específicamente a través de la propiedad ''' ''' ''' Un objeto [KeyGesture] que describe el juego de teclas que activa el command ''' Valor por defecto [Nothing] ''' Public Property KeyGesture As KeyGesture = Nothing ''' ''' ¿¿ Se disparan los eventos [ExecuteStart] y [ExecuteCompleted] ?? ''' [True] - si que se disparan, [False] - (Valor predeterminado, [False] no se disparan esos eventos ''' ''' ''' [True] - si que se disparan, [False] - (Valor predeterminado, [False] no se disparan esos eventos ''' Valor por defecto [False] ''' Public Property DispararEventosExecuterStartCompleted As Boolean = False ''' ''' El command se esta ejecutando??? ''' [True] si , se esta ejecutando. [False], no se esta ejecutando ''' ''' ''' [True] si , se esta ejecutando. [False], no se esta ejecutando ''' Valor por defecto [False] ''' Public Property EstaEjecutandose As Boolean = False ''' ''' Devuelve una referencia a este Command ''' Public ReadOnly Property GetCommand As ICommand Get Return Me End Get End Property #End Region #End Region #Region "Constructores" ''' ''' Constructor para las clases derivadas unicamente ''' Protected Sub New() 'Me.New(String.Empty, Nothing, Nothing) End Sub ''' ''' Constructor ''' ''' ''' Encapsula un método que devuelve un valor Lógico ''' Es el método que devuelve el valor [CanExecute] ''' Public Sub New(ByVal execAction As Action(Of Object)) Me._executeAction = execAction End Sub ''' ''' Constructor ''' ''' ''' Encapsula un método que devuelve un valor Lógico ''' Es el método que devuelve el valor [CanExecute] ''' ''' ''' Encapsula un método que no tiene parámetros y no devuelve un valor ''' Es el método que se tiene que ejecutar [Execute] ''' Public Sub New(ByVal execAction As Action(Of Object), ByVal canExecFunc As Predicate(Of Object)) Me._executeAction = execAction Me._canExecuteAction = canExecFunc End Sub #End Region #Region " Implementación Icommand" ''' ''' Se dispara cuando se producen cambios que afectan a si el comando se debe ejecutar. ''' ''' ''' ''' Normalmente, si no se puede ejecutar el comando, el origen de comando se deshabilita. ''' 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(value As EventHandler) If IsNothing(Me._canExecuteAction) = False Then AddHandler CommandManager.RequerySuggested, value End If End AddHandler RemoveHandler(value As EventHandler) If IsNothing(Me._canExecuteAction) = False Then AddHandler CommandManager.RequerySuggested, value End If End RemoveHandler RaiseEvent(sender As Object, e As EventArgs) End RaiseEvent End Event ''' ''' Variable local que contiene el valor que devuelve la ejecución de la función [CanExecute] ''' Se usa para saber si hay que disparar el evento [CanExecuteChanged] o no ''' tiene el valor True, porque soy optimista y supongo que siempre se puede ejecutar ''' Protected Friend campoCanExecuteAnterior As Boolean = True ''' ''' Define el método que determina si el comando se puede ejecutar en su estado actual. ''' ''' ''' Tipo: System.Object ''' Datos utilizados por el comando. ''' Si el comando no requiere datos para pasar, este objeto se puede ''' establecer en referencia null (Nothing en Visual Basic). ''' ''' ''' Tipo: System.Boolean ''' true si este comando se puede ejecutar; si no, false. ''' ''' ''' ''' Normalmente, un origen de comando llama al método de ''' CanExecute cuando se provoca el evento de CanExecuteChanged. ''' ''' Public Function CanExecute(ByVal parameter As Object) As Boolean Implements System.Windows.Input.ICommand.CanExecute '---------------------- ' A) El resultado. Dim localCanExecuteActual As Boolean = False '---------------------- ' B) El control de si se puede ejecutar o no If (_canExecuteAction Is Nothing) Then localCanExecuteActual = True Else localCanExecuteActual = _canExecuteAction(parameter) End If '---------------------- 'C) Mirar si se dispara el evento o no If Boolean.Equals(campoCanExecuteAnterior, localCanExecuteActual) = False Then campoCanExecuteAnterior = localCanExecuteActual Call OnCanExecuteChanged() End If '---------------------- 'D) Devolver el resultado Return localCanExecuteActual End Function ''' ''' Define el método que se llamará cuando se invoca el comando. ''' ''' ''' Tipo: System.Object ''' Datos utilizados por el comando. ''' Si el comando no requiere datos para pasar, este objeto se puede ''' establecer en referencia null (Nothing en Visual Basic). ''' ''' Public Sub Execute(ByVal parameter As Object) Implements System.Windows.Input.ICommand.Execute EstaEjecutandose = True If DispararEventosExecuterStartCompleted = False Then '------------------- ' NO ' No se disparan los eventos [ExecuteStart] y [ExecuteCompleted] _executeAction(parameter) Else '------------------- 'SI ' si que se disparan eventos [ExecuteStart] y [ExecuteCompleted] ' excepción que se mandara por el evento [Final del proceso] Dim exOut As System.Exception = Nothing Try ' Disparar el evento [Inicio del proceso] Call OnExecuteStart(parameter) ' ejecutar el command _executeAction(parameter) Catch ex As System.Exception 'reenviar la excepción como un parámetro a través del evento [RaiseExecutionCompleted] If ex.InnerException Is Nothing Then exOut = ex Else exOut = ex.InnerException End If Finally ' Disparar el evento [Final del proceso] Call OnExecuteCompleted(parameter, exOut) End Try End If ' / eof [DispararEventosExecuterStartCompleted] EstaEjecutandose = False End Sub #End Region #Region "Eventos" ''' ''' Se produce cuando la operación[execute] empieza su ejecución ''' Public Event ExecuteStart As EventHandler(Of DelegateCommandEjecutadoEventArgs) ''' ''' Se produce cuando la operación[execute] ha finalizado, se ha cancelado o ha producido una excepción ''' Public Event ExecuterCompleted As EventHandler(Of DelegateCommandEjecutadoEventArgs) ''' ''' Dispara el evento [CanExecuteChanged] ''' Protected Friend Sub OnCanExecuteChanged() RaiseEvent CanExecuteChanged(Me, EventArgs.Empty) End Sub ''' ''' Dispara el evento [ExecuteStart] ''' Protected Friend Sub OnExecuteStart(ByVal parameter As Object) 'If DispararEventosExecuterStartCompleted = True Then RaiseEvent ExecuteStart(Me, New DelegateCommandEjecutadoEventArgs(parameter, Nothing)) 'End If End Sub ''' ''' Dispara el evento [ExecuterCompleted] ''' Protected Friend Sub OnExecuteCompleted(ByVal parameter As Object, ByVal ex As System.Exception) 'If DispararEventosExecuterStartCompleted = True Then RaiseEvent ExecuterCompleted(Me, New DelegateCommandEjecutadoEventArgs(parameter, ex)) 'End If End Sub #End Region #Region "IDisposable Support. [Para clases normales y bases ] Implementación de .NET Version: [2022/03/03]" ''' ''' Variable local para detectar llamadas redundantes que indica [True] el objeto se ha eliminado, [False], en caso contrario. ''' Private campoDisposedValue As System.Boolean ''' ''' Obtiene un valor que indica si el objeto se ha eliminado. ''' ''' ''' Tipo: Boolean ''' Es true si objeto se ha eliminado; en caso contrario, es false. ''' Public Overloads ReadOnly Property IsDisposed() As System.Boolean Get Return campoDisposedValue End Get End Property ''' ''' [Dispose] Código de limpieza de la clase ''' ''' [True], llama el usuario, [False], llama el sistema ''' ''' ''' Dispose (bool quienMeLLama) se ejecuta en dos escenarios distintos. ''' ''' ''' Si [quienMeLLama] es igual a true, el método ha sido llamado ''' directamente, o indirectamente, por el código de un usuario. ''' Los recursos administrados y no administrados se pueden eliminar. ''' ''' ''' Si [quienMeLLama] es igual a false, el método ha sido llamado por el ''' [runtime] desde el interior del finalizador y usted no debe hacer ''' referencia a otros objetos, ni siquiera para eliminarlos. ''' Solo los recursos no administrados pueden ser eliminados. ''' ''' Protected Overridable Overloads Sub Dispose(paramQuienMeLLama As System.Boolean) If Me.campoDisposedValue = False Then If paramQuienMeLLama = True Then Me.campoDisposedValue = True '------------------------------------------------------------------------------------------ ' 1) Liberar UNICAMENTE objetos NO administrados. ' Son recursos que NO administra el motor en tiempo de ejecución, ' como conexiones a bases de datos ' llamadas al API de Windows, etc. el Word o Excel '------------------------------------------------------------------------------------------ 'Call LiberarRecursosNoAdministrados() '------------------------------------------------------------------------------------------ ' 2) Desechar objetos SI administrados. ' Un objeto administrado es cualquier objeto de .NET ' es un objeto de .NET que maneja el motor de ejecución de NET '------------------------------------------------------------------------------------------ If Not (Me._executeAction Is Nothing) Then Me._executeAction = Nothing End If If Not (Me._canExecuteAction Is Nothing) Then Me._canExecuteAction = Nothing End If If Not (Me.KeyGesture Is Nothing) Then Me.KeyGesture = Nothing End If '------------------------------------------------------------------------------------------ ' 3) Establecer campos grandes como Null. '------------------------------------------------------------------------------------------ Me.ComandoNombre = Nothing Me.ComandoDescripcion = Nothing Me.DispararEventosExecuterStartCompleted = Nothing Me.EstaEjecutandose = Nothing Else '------------------------------------------------------------------------------------------ ' 1) Liberar UNICAMENTE los objetos NO administrados. '------------------------------------------------------------------------------------------ 'Call LiberarRecursosNoAdministrados() End If End If End Sub ' ''' ' ''' 1) Liberar objetos NO administrados. ' ''' Son recursos que NO administra el motor en tiempo de ejecución, ' ''' como conexiones a bases de datos ' ''' llamadas al API de Windows, etc. el Word o Excel ' ''' ' ''' ' ''' ATENCION - Quite los comentarios de Finalize() SOLO si el anterior Dispose(disposing As Boolean) ' ''' tiene código para liberar recursos no administrados. ' ''' ' Private Sub LiberarRecursosNoAdministrados() ' ' si no hay recursos, no hacer nada ' End Sub ' ''' ' ''' Permite que un objeto intente liberar recursos NO ADMINISTRADOS y realizar otras operaciones de limpieza ' ''' antes de que sea reclamado por la recolección de elementos no utilizados. ' ''' ' ''' ' ''' ATENCION - Quite los comentarios de Finalize() SOLO si el anterior Dispose(disposing As Boolean) ' ''' tiene código para liberar recursos no administrados. ' ''' ' Protected Overrides Sub Finalize() ' 'ATENCION - Quite los comentarios de Finalize() SOLO si el anterior Dispose(disposing As Boolean) ' ' tiene código para liberar recursos no administrados. ' ' No cambie este código. Coloque el código de limpieza en el anterior Dispose(disposing As Boolean). ' Dispose(False) ' MyBase.Finalize() ' End Sub ''' ''' [Dispose] Código de limpieza de la clase ''' ''' ''' Visual Basic agregó este código para implementar correctamente el patrón descartable. ''' No cambie este código. Coloque el código de limpieza en Dispose(disposing As Boolean). ''' Public Overloads Sub Dispose() Implements IDisposable.Dispose Me.Dispose(True) 'Este objeto se limpiará con el método Dispose. 'Por lo tanto, debe llamar a GC.SupressFinalize para 'quitar este objeto de la cola de finalización 'y evitar que el código de finalización para este objeto 'se ejecute por segunda vez. GC.SuppressFinalize(Me) End Sub #End Region End Class ''' ''' Evento personalizado que informa sobre el estado de ejecución del [Command] en su tarea [execute] ''' Public Class DelegateCommandEjecutadoEventArgs Inherits System.EventArgs Private ReadOnly _ex As System.Exception = Nothing Private ReadOnly _parameter As Object = Nothing ''' ''' La excepción que se produce (Si existe), su valor predeterminado es [Nothing] ''' Public ReadOnly Property [Exception] As Object Get Return _ex End Get End Property ''' ''' El parámetro que recibe el command, (Si existe), su valor predeterminado es [Nothing] ''' Public ReadOnly Property Parameter As Object Get Return _parameter End Get End Property ''' ''' [True] si no hay problemas , si no hay ningún error [Command.Excecute] se ejecuta sin problemas ''' [False] en caso contrario. ''' Public ReadOnly Property IsValid As Boolean Get If Me._ex Is Nothing Then Return True Else Return False End If End Get End Property ''' ''' Constructor ''' ''' El parámetro que recibe el command (o un valor nothing) ''' La excepción que se produce (o un valor nothing) ''' Public Sub New(ByVal parameter As Object, ByVal ex As System.Exception) MyBase.New() Me._ex = ex Me._parameter = parameter End Sub End Class End Namespace