更新的标题:为什么ICommand.CanExecute一直被调用,而不是像事件那样工作?

时间:2013-09-23 14:40:46

标签: wpf vb.net mvvm icommand

我在WPF中采用MVVM模式并学习了Command的使用。但是在我的实现中,我总是调用我分配给实现CanExecute的委托。我的意思是如果我在委托函数中放置一个断点,它表明这个函数不断被调用。根据我的理解(以及一种自然的思维方式,但当然我可能是错的),只有当我以某种方式通知状态的变化时才会调用此委托,而CommandManager(重新)检查{{ 1}}属性并修改UI元素的CanExecute属性。

这是我对VB.NET的实现,我最初是从C#版本获得的。我注意到我需要对移植的代码进行一些更改才能进行编译。难道它是C#和VB.NET的底层是不同的?那么有人可以为我提供一个原始的VB.NET实现,或者指出我有什么问题,或者如果我正确理解Command行为会怎么做?

这是我的VB.NET版本:

IsEnabled

我如何实例化一个对象是这样的:

 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

当然CanExecuteExec的签名如下:

MyCommand = New CommandBase(AddressOf CommandExec, AddressOf CanExecuteExec)

就像我提到的那样,Private Function CanExecuteExec(obj As Object) As Boolean 一直在被召唤。我想这是低效的,想象我有数百个CanExecuteExec个对象,而且大多数Command个对象在大多数情况下都不会被改变。

更新:

有人说CanExecute确实一直被召唤,而其他人却反其道而行之。我不是这方面的专家,但我不得不说第二种意见听起来更自然,对我来说更有意义。虽然我仍然需要弄清楚这是否属实,但为什么WPF会一直检测到这种变化,以便它继续检查CanExecute

3 个答案:

答案 0 :(得分:10)

在您的CanExecuteDelegate中,您已与CommandManager.RequerySuggested挂钩。

因此,每当CommandManager.RequerySuggested被提出时,您的CanExecuteDelegate都会被调用。

  

只要命令检测到对命令源的更改,就会引发CommandManager.RequerySuggested事件   管理器,范围从 Keyboard.KeyUpEvent Mouse.ClickEvent 等。

此外,CommandManager有一个静态方法 - InvalidateRequerySuggested,它强制CommandManager引发RequerySuggestedEvent。因此,您可以调用它来手动验证命令。

如果你想拿着控制来提升CanExecute,你可以使用PRISM提供的Delegate Command。仅当您明确调用Delegate Command公开的CanExecute方法时,才会调用RaiseCanExecuteChanged()委托。

合并评论以回答

  

从那以后每次转向VS时都会遇到断点   CommandManager RequerySuggested事件在失去焦点时被调用   窗口和窗口的激活属性已更改。这就是你的原因   请注意,当您移动到断点时,断点正在经常发生   VS因为焦点从WPF窗口移动到Visual Studio。

答案 1 :(得分:2)

当您设置命令时,运行时没有可靠的方法来了解您的CanExecute将依赖哪些数据来做出决定。因此,当您拥有绑定到UI并在CommandManager中注册的命令时,行为是,只要应用程序的状态发生更改,就会重新评估所有命令的CanExecute。 WPF知道这一点的方式是更新绑定属性或发生UI事件时。

通常,只要绑定更新或发生某些控件事件,您就会看到调用CanExecute(例如,当文本框的文本突出显示时,内置CanExecute的{​​{1}}和{ {1}}命令会发生变化,因此突出显示事件会触发重新评估,我认为该重新评估会绑定到Cut事件。

答案 2 :(得分:0)

可能由于未知原因UI可能会更新(测量,排列,然后渲染调用)。如果你设置了断点,可以执行方法,它将重新发生。换句话说,你无法通过这个断点,每次你做F5时,断点都会再次出现。

为了调查你应该把log / output语句放在你的can execute方法中,以及调用它的次数和时间。