Updated title: Why does ICommand.CanExecute ring all the time, but does not work as an event?

I accept the MVVM pattern in WPF and learned about using Command . But in my implementation, the delegate assigned to the CanExecute implementation is always called. I mean, if I set a breakpoint inside the delegate function, this shows that this function continues to receive calls. In my understanding (and natural thinking, but of course I can be wrong), this delegate is called only when I somehow notify about a state change and that when CommandManager (re) checks the CanExecute property and change the IsEnabled property of the interface element.

Here is my VB.NET implementation that I got from the C # version. I noticed that I need to make some changes to the ported code so that it can compile. Could the basis of C # and VB.NET be different? So can someone provide me with the original implementation of VB.NET or tell me what is wrong, or if I understand the behavior of the command correctly?

Here is my version of VB.NET:

  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 as I instantiate the object, it is something like this:

 MyCommand = New CommandBase(AddressOf CommandExec, AddressOf CanExecuteExec) 

where CanExecuteExec, of course, has this signature:

 Private Function CanExecuteExec(obj As Object) As Boolean 

As I mentioned, calling CanExecuteExec calls all the time. I think this is inefficient, imagine that I have hundreds of Command objects, and most of them CanExecute no longer change.

UPDATE:

Someone says that CanExecute really CanExecute called all the time, while others say the opposite. I am not an expert in this, but I must say that the second opinion sounds more natural and makes more sense to me. Although I still need to find out if this is true, why WPF constantly detects a change so that it continues to validate CanExecute

+4
source share
3 answers

In CanExecuteDelegate you have binding to CommandManager.RequerySuggested .

So, whenever CommandManager.RequerySuggested , you call your CanExecuteDelegate .

The CommandManager.RequerySpected event occurs every time the command source parameters are changed by the manager command, which varies from Keyboard.KeyUpEvent, Mouse.ClickEvent, etc.

In addition, the CommandManager has a static method - InvalidateRequerySuggested , which forces the CommandManager to raise RequerySovedEvent. So you can call this to check your commands too manually.

If you want to take control on hand to enhance CanExecute, you can use the delegation command provided by PRISM. CanExecute delegate will only be called when you explicitly call the RaiseCanExecuteChanged() method opened by the delegation command.

Include comments for the response

A breakpoint is pressed each time it is rotated to VS with a CommandManager RequerySpected event that causes a window to be lost in focus and the activation property has changed from the window. That's why you notice that a breakpoint clicks from time to time when you switch to VS, as the focus moves from the WPF window to Visual Studio.

+7
source

When you set up your command, there is no reliable way for the runtime to know what data your CanExecute will rely on to make a decision. So, when you have commands that are associated with your user interface and registered with the CommandManager , the behavior is that the CanExecute for all are overridden whenever your application state changes. The WPF method is aware of this when a related property is updated or a user interface event occurs.

Typically, you will call CanExecute each time the bindings are updated or when certain control events occur (for example, when the text of the text field is highlighted, the CanExecute commands of the built-in Cut and Copy commands will change, and therefore the highlight event will cause a re-evaluation, which I believe is related to MouseUp event).

+2
source

Perhaps, for some reason, the user interface may be updated (measuring, organizing, and then calling Render). And if you have a breakpoint that the method can execute, it will be repeated. In other words, you cannot pass this breakpoint, every time you do F5, the breakpoint will be again.

To investigate, you must put the logging / output statements in your execution method, how many times and when it will be called.

0
source

All Articles