6

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

tete
  • 4,859
  • 11
  • 50
  • 81
  • 1
    What is your question? `CanExecute` *does* get called all the time - your understanding is not quite correct - but you haven't actually asked a question here. – Dan Puzey Sep 23 '13 at 14:48
  • @DanPuzey is right, you are not understanding correctly, the can-execute delegates are always called. – J King Sep 23 '13 at 15:34
  • @tete: you posted your update, but my question would be: when do you think your command `CanExecute` should "naturally" be called? How do you work this out logically - remembering that someone has to write code that will function for all scenarios, for all applications, for all commands, for all possible data sources and UI complexities? Without knowing the content of the `CanExecute` method, the only option is to requery *a lot*. – Dan Puzey Sep 24 '13 at 07:47
  • I think it is well explained in the second answer in this query. http://stackoverflow.com/questions/14294738/when-is-canexecute-called – sandeep vidiyala Nov 14 '16 at 06:35

3 Answers3

13

In your CanExecuteDelegate you have hook to CommandManager.RequerySuggested.

So, whenever CommandManager.RequerySuggested is raised your CanExecuteDelegate will be called.

CommandManager.RequerySuggested event is raised whenever changes to the command source are detected by the command manager which ranges from Keyboard.KeyUpEvent, Mouse.ClickEvent etc.

Also, CommandManager has a static method - InvalidateRequerySuggested which forces the CommandManager to raise the RequerySuggestedEvent. So, you can call that to validate your commands too manually.

If you want to take the control in hand for raising CanExecute, you can use the Delegate Command provided by PRISM. CanExecute delegate will get called only when you explicitly call RaiseCanExecuteChanged() method exposed by Delegate Command.

Incorporating comments to answer

Breakpoint is hitting every time on turning to VS since CommandManager RequerySuggested event gets called on lost focus of window and on activation property changed of window. That's why you notice that breakpoint is hitting every now and then when you move to VS since focus moves from WPF window to Visual Studio.

Rohit Vats
  • 79,502
  • 12
  • 161
  • 185
  • 1
    Thanks for your question. Finally somebody understood what I am asking. Actually if you look at my code, I did call the CommandManager.InvalidateRequerySuggested in the CanExecuteChanged event. Is it the correct place to make this function call? Because if I remember correctly, the CanExecute delegate still gets called all the time. But I will verify it tomorrow – tete Sep 23 '13 at 21:18
  • And regarding to using Command from Prism, does it mean that the usage needs to be more careful, because now it is totally the developer's responsibility to directly maintain the CanExecute state? Currently I still can't persuade our team to use Prism. And I really don't know whether I should put this point into Pro or Con :) – tete Sep 23 '13 at 21:36
  • 1
    In your implementation you don't need to call `CommandManager.InvalidateRequerySuggested` since CommandManager smart enough to detect conditions when it needs to be raised. Moreover, your break point is hitting every time since `CanExecute` executes even on Window's lost focus. – Rohit Vats Sep 24 '13 at 05:48
  • If you want I can post the `DelegateCommand` code used in PRISM. Incorporating PRISM in your application just to use inbuilt `DelegateCommand` definitely not a reason to switch to PRISM. PRISM is much more than that DI, loose coupling etc. – Rohit Vats Sep 24 '13 at 06:25
  • 1
    Thanks for pointing out that CanExecute is actually called when Windows has lost focus. I think that's the reason! Because if I remove the breaking point but count the hitting number by e.g., printing in debug window inside this delegate, I can see that it is in fact NOT CALLED if I do nothing. So I guess it totally depends on how CommandManager sees whether it is needed to re-check the CanExecute. Please update this point (the function is called because I put a break point and thereby switch the focus from app to VS) in your original post so the answer is more clear – tete Sep 24 '13 at 06:55
  • So I guess it means there is nothing wrong with my implementation in ICommand and my ViewModel. And if I still feels uncomfortable about the "not smart" behavior by the CommandManager, I can turn to PRISM for fully control of CanExecute. Like I said our team still has not decided to use it because we are worried about the extra complexity. But IMO it is inevitable to use such framework since it is not easy to only use WPF, or WPF+MVVM even for a SW of moderate to large scale. – tete Sep 24 '13 at 07:00
  • I have incorporated comments in my answer and yeah I see no issues in your version of ICommand. Other way out would be to extract `DelegateCommand` version from PRISM dll and use that in your application instead of using complete PRISM in your application. – Rohit Vats Sep 24 '13 at 07:45
3

When you set up your command, there's no reliable way for the runtime to know what data your CanExecute will rely on in order to make its decision. So, when you have commands that are bound into your UI and are registered in the CommandManager, the behaviour is that the CanExecute for all commands is re-evaluated whenever the state of your application changes. The way WPF knows about this is when a bound property is updated, or when a UI event occurs.

Typically you'll see CanExecute called whenever bindings update or when certain control events occur (for example, when a textbox's text is highlighted, the CanExecute of the inbuilt Cut and Copy commands will change, and so the highlight event triggers a re-evaluation that I would imagine is bound to the MouseUp event).

Dan Puzey
  • 33,626
  • 4
  • 73
  • 96
  • Thanks for your reply. So basically what you are saying is WPF will call this delegate whenever any change happens to the ViewModel, even if such change might be irrelevant to the state of the Command itself? I guess your description does fit my observation. But could you give me the reference to such behavior, so that I can dig if I made my ViewModel badly that WPF thinks the state keeps changing? – tete Sep 23 '13 at 21:26
  • As Rohit suggested in his answer, the event `CommandManager.RequerySuggested` will drive this for most `RoutedCommands` or `CommandBinding` attached commands. This is a single static method that is shared by all commands, and so yes: it will cause your delegate to be called when the state change might be irrelevant to a specific command. But then, it would have no way of knowing what state change *is* relevant to your command, so it would have to fire the event for many possible changes. – Dan Puzey Sep 24 '13 at 07:50
  • Dan, good point. I think currently I will stick with the default solution of letting CommandManager decide whether the CanExecute should be re-checked. Simply because I don't want to keep in mind that I should raise the event of CanExecuteChanged in all possible scenarios. So I will use it until I find it inefficient. After all, it is not as stupid as I thought it was: as I mentioned in the accepted answer, it is only because I was jumping between the app and VS which triggered LostFocus/MouseOver change so the delegate was re-called. – tete Sep 24 '13 at 14:18
0

May be for unknown reason the UI could be getting updated (Measure, Arrange, and then Render calls). And if you have a breakpoint set on can execute method it'll be re-occurring. In other words you can't get pass this break point, each time you would do F5, the break point will it again.

To investigate you should put the logging/output statements in your can execute method and how many times and when it is getting called.

Rajiv
  • 1,426
  • 14
  • 21