[wpf] Binding Button click to a method

I have a datagrid bound to an observable collection of objects. What I want to do is have a button that will execute a method of the object representing the row of the button that was clicked. So what I have now is something like this:

            <DataGridTemplateColumn Header="Command">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Button Name="cmdCommand" Click="{Binding Command}" 
                                Content="Command"/>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>

Which doesn't work and reports the following error:

Click="{Binding Command}" is not valid. '{Binding Command}' is not a valid event handler method name. Only instance methods on the generated or code-behind class are valid.

I've looked at command binding but that looks like it would just end up going to a single external command instead of to the object bound to the row. I have it working using an event handler on the code behind and then routing it to the item bound to the selected row (since the row gets selected when the button is clicked) but that seems like poor way of handing this and I assume I'm just missing something here.

This question is related to wpf

The answer is


Some more explanations to the solution Rachel already gave:

"WPF Apps With The Model-View-ViewModel Design Pattern"

by Josh Smith

http://msdn.microsoft.com/en-us/magazine/dd419663.aspx


Here is the VB.Net rendition of Rachel's answer above.

Obviously the XAML binding is the same...

<Button Command="{Binding Path=SaveCommand}" />

Your Custom Class would look like this...

''' <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

And finally the RelayCommand class is as follows...

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

Hope that helps any VB.Net developers!


Click is an event. In your code behind, you need to have a corresponding event handler to whatever you have in the XAML. In this case, you would need to have the following:

private void Command(object sender, RoutedEventArgs e)
{

}

Commands are different. If you need to wire up a command, you'd use the Commmand property of the button and you would either use some pre-built Commands or wire up your own via the CommandManager class (I think).


You have various possibilies. The most simple and the most ugly is:

XAML

<Button Name="cmdCommand" Click="Button_Clicked" Content="Command"/> 

Code Behind

private void Button_Clicked(object sender, RoutedEventArgs e) { 
    FrameworkElement fe=sender as FrameworkElement;
    ((YourClass)fe.DataContext).DoYourCommand();     
} 

Another solution (better) is to provide a ICommand-property on your YourClass. This command will have already a reference to your YourClass-object and therefore can execute an action on this class.

XAML

<Button Name="cmdCommand" Command="{Binding YourICommandReturningProperty}" Content="Command"/>

Because during writing this answer, a lot of other answers were posted, I stop writing more. If you are interested in one of the ways I showed or if you think I have made a mistake, make a comment.