I am adopting MVVM pattern in WPF and have learned the use of Command
. But in my implementation, the delegate I assigned to implement CanExecute
is always called. I mean if I put a break point inside the delegate function, it shows that this function keeps getting called. To my understanding (and a natural way of thinking, but of course I can be wrong), this delegate only gets called when I somehow notifies the change of the state and that's when the CommandManager
(re)checks the CanExecute
property and modify the IsEnabled
property of the UI element.
Here is my implementation of VB.NET, which I got originally from a C# version. I did notice that I needed to make some change to the ported code in order for it to compile. Could it be the underlying of C# and VB.NET is different? So can somebody provide me a original VB.NET implementation, or point me out what is wrong or do if I understand the Command behavior correctly?
Here is my VB.NET version:
Public Class CommandBase
Implements ICommand
Public Property ExecuteDelegate() As Action(Of Object)
Public Property CanExecuteDelegate() As Predicate(Of Object)
Public Sub New()
End Sub
Public Sub New(execute As Action(Of Object))
Me.New(execute, Nothing)
End Sub
Public Sub New(execute As Action(Of Object), canExecute As Predicate(Of Object))
If execute Is Nothing Then
Throw New ArgumentNullException("execute")
End If
ExecuteDelegate = execute
CanExecuteDelegate = canExecute
End Sub
Public Function CanExecute(parameter As Object) As Boolean Implements ICommand.CanExecute
Return If(CanExecuteDelegate Is Nothing, True, CanExecuteDelegate(parameter))
End Function
Public Custom Event CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged
AddHandler(ByVal value As EventHandler)
If CanExecuteDelegate IsNot Nothing Then
AddHandler CommandManager.RequerySuggested, value
End If
End AddHandler
RemoveHandler(ByVal value As EventHandler)
If CanExecuteDelegate IsNot Nothing Then
RemoveHandler CommandManager.RequerySuggested, value
End If
End RemoveHandler
RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)
CommandManager.InvalidateRequerySuggested()
End RaiseEvent
End Event
Public Sub Execute(parameter As Object) Implements ICommand.Execute
If ExecuteDelegate IsNot Nothing Then ExecuteDelegate.Invoke(parameter)
End Sub
Public Sub RaiseCanExecuteChanged()
CommandManager.InvalidateRequerySuggested()
End Sub
End Class
And how I instantiate an object is something like this:
MyCommand = New CommandBase(AddressOf CommandExec, AddressOf CanExecuteExec)
where the CanExecuteExec of course has the signature like this:
Private Function CanExecuteExec(obj As Object) As Boolean
Like I mentioned, the CanExecuteExec
is getting called all the time. I guess it is inefficient, imagine that I have hundreds of Command
objects and most of the CanExecute
of them don't get changed most of the time.
UPDATE:
Somebody says the CanExecute
indeed gets called all the time, while others say the opposite. I am no expert on this but I have to say the second opinion sounds more natural and makes more sense to me. Although I still need to figure out if that is true, why WPF detects the change all the time so that it keeps checking the CanExecute