wpf - propiedades - eventos de button
Botón de enlace, haga clic en un método (5)
Algunas explicaciones más a la solución que Rachel ya dio:
"Aplicaciones de WPF con el patrón de diseño Model-View-ViewModel"
por Josh Smith
Tengo una cuadrícula de datos vinculada a una colección observable de objetos. Lo que quiero hacer es tener un botón que ejecutará un método del objeto que representa la fila del botón que se hizo clic. Entonces, lo que tengo ahora es algo como esto:
<DataGridTemplateColumn Header="Command">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Name="cmdCommand" Click="{Binding Command}"
Content="Command"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Lo cual no funciona e informa el siguiente error:
Click = "{Binding Command}" no es válido. ''{Binding Command}'' no es un nombre de método de controlador de eventos válido. Solo los métodos de instancia en la clase generada o de código subyacente son válidos.
He observado el enlace de comandos, pero parece que acabaría yendo a un único comando externo en lugar de al objeto vinculado a la fila. Lo tengo trabajando utilizando un controlador de eventos en el código subyacente y luego enrutarlo al elemento vinculado a la fila seleccionada (ya que la fila se selecciona cuando se hace clic en el botón) pero parece una forma pobre de manejar esto y asumo que '' Me estoy perdiendo algo aquí.
Aquí está la versión de VB.Net de la respuesta de Rachel anterior.
Obviamente, el enlace XAML es el mismo ...
<Button Command="{Binding Path=SaveCommand}" />
Su clase personalizada se vería así ...
'''''' <summary>
'''''' Retrieves an new or existing RelayCommand.
'''''' </summary>
'''''' <returns>[RelayCommand]</returns>
Public ReadOnly Property SaveCommand() As ICommand
Get
If _saveCommand Is Nothing Then
_saveCommand = New RelayCommand(Function(param) SaveObject(), Function(param) CanSave())
End If
Return _saveCommand
End Get
End Property
Private _saveCommand As ICommand
'''''' <summary>
'''''' Returns Boolean flag indicating if command can be executed.
'''''' </summary>
'''''' <returns>[Boolean]</returns>
Private Function CanSave() As Boolean
'' Verify command can be executed here.
Return True
End Function
'''''' <summary>
'''''' Code to be run when the command is executed.
'''''' </summary>
'''''' <remarks>Converted to a Function in VB.net to avoid the "Expression does not produce a value" error.</remarks>
'''''' <returns>[Nothing]</returns>
Private Function SaveObject()
'' Save command execution logic.
Return Nothing
End Function
Y finalmente, la clase RelayCommand es la siguiente ...
Public Class RelayCommand : Implements ICommand
ReadOnly _execute As Action(Of Object)
ReadOnly _canExecute As Predicate(Of Object)
Private Event ICommand_CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged
'''''' <summary>
'''''' Creates a new command that can always execute.
'''''' </summary>
'''''' <param name="execute">The execution logic.</param>
Public Sub New(execute As Action(Of Object))
Me.New(execute, Nothing)
End Sub
'''''' <summary>
'''''' Creates a new command.
'''''' </summary>
'''''' <param name="execute">The execution logic.</param>
'''''' <param name="canExecute">The execution status logic.</param>
Public Sub New(execute As Action(Of Object), canExecute As Predicate(Of Object))
If execute Is Nothing Then
Throw New ArgumentNullException("execute")
End If
_execute = execute
_canExecute = canExecute
End Sub
<DebuggerStepThrough>
Public Function CanExecute(parameters As Object) As Boolean Implements ICommand.CanExecute
Return If(_canExecute Is Nothing, True, _canExecute(parameters))
End Function
Public Custom Event CanExecuteChanged As EventHandler
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 Object, ByVal e As EventArgs)
If (_canExecute IsNot Nothing) Then
_canExecute.Invoke(sender)
End If
End RaiseEvent
End Event
Public Sub Execute(parameters As Object) Implements ICommand.Execute
_execute(parameters)
End Sub
End Class
¡Espero que ayude a cualquier desarrollador de VB.Net!
Click es un evento. En el código que está detrás, debe tener un controlador de eventos correspondiente a lo que tenga en el XAML. En este caso, necesitaría tener lo siguiente:
private void Command(object sender, RoutedEventArgs e)
{
}
Los comandos son diferentes. Si necesita cablear un comando, usaría la propiedad Commmand del botón y usaría algunos Comandos preconstruidos o conectaría el suyo a través de la clase CommandManager (creo).
Hago esto todo el tiempo. Aquí hay un vistazo a un ejemplo y cómo lo implementaría.
Cambie su XAML para usar la propiedad Command del botón en lugar del evento Click. Estoy usando el nombre SaveCommand ya que es más fácil seguir algo llamado Command.
<Button Command="{Binding Path=SaveCommand}" />
Su CustomClass al que el botón está obligado ahora debe tener una propiedad llamada SaveCommand de tipo ICommand
. Necesita señalar el método en CustomClass que desea ejecutar cuando se ejecuta el comando.
public MyCustomClass
{
private ICommand _saveCommand;
public ICommand SaveCommand
{
get
{
if (_saveCommand == null)
{
_saveCommand = new RelayCommand(
param => this.SaveObject(),
param => this.CanSave()
);
}
return _saveCommand;
}
}
private bool CanSave()
{
// Verify command can be executed here
}
private void SaveObject()
{
// Save command execution logic
}
}
El código anterior usa un RelayCommand que acepta dos parámetros: el método para ejecutar, y un valor verdadero / falso de si el comando puede ejecutarse o no. La clase RelayCommand es un archivo .cs separado con el código que se muestra a continuación. Lo obtuve de Josh Smith :)
/// <summary>
/// A command whose sole purpose is to
/// relay its functionality to other
/// objects by invoking delegates. The
/// default return value for the CanExecute
/// method is ''true''.
/// </summary>
public class RelayCommand : ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
#region Constructors
/// <summary>
/// Creates a new command that can always execute.
/// </summary>
/// <param name="execute">The execution logic.</param>
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
/// <summary>
/// Creates a new command.
/// </summary>
/// <param name="execute">The execution logic.</param>
/// <param name="canExecute">The execution status logic.</param>
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameters)
{
return _canExecute == null ? true : _canExecute(parameters);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameters)
{
_execute(parameters);
}
#endregion // ICommand Members
}
Tienes varias posibilidades. El más simple y el más feo es:
XAML
<Button Name="cmdCommand" Click="Button_Clicked" Content="Command"/>
Código detrás
private void Button_Clicked(object sender, RoutedEventArgs e) {
FrameworkElement fe=sender as FrameworkElement;
((YourClass)fe.DataContext).DoYourCommand();
}
Otra solución (mejor) es proporcionar una propiedad ICommand en su YourClass
. Este comando ya tendrá una referencia a su objeto YourClass
y, por lo tanto, puede ejecutar una acción en esta clase.
XAML
<Button Name="cmdCommand" Command="{Binding YourICommandReturningProperty}" Content="Command"/>
Porque durante la redacción de esta respuesta, se publicaron muchas otras respuestas, dejo de escribir más. Si está interesado en alguna de las formas en que aparecí o si cree que cometí un error, haga un comentario.