6

我在 WPF 中采用 MVVM 模式,并且已经学会了Command. 但是在我的实现中,我分配给实现的委托CanExecute总是被调用。我的意思是,如果我在委托函数中放置一个断点,则表明该函数不断被调用。CommandManager据我了解(也是一种自然的思维方式,但当然我可能是错的),只有当我以某种方式通知状态更改并且(重新)检查CanExecute属性并修改IsEnabled属性时,才会调用此委托用户界面元素。

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

这是我的 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

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

MyCommand = New CommandBase(AddressOf CommandExec, AddressOf CanExecuteExec)

CanExecuteExec 当然有这样的签名:

Private Function CanExecuteExec(obj As Object) As Boolean

就像我提到的,CanExecuteExec一直被调用。我想这是低效的,想象一下我有数百个Command对象,其中大多数CanExecute大部分时间都没有改变。

更新:

有人说CanExecute确实总是被调用,而另一些人则相反。我不是这方面的专家,但我不得不说第二种意见听起来更自然,对我来说更有意义。虽然我仍然需要弄清楚这是否属实,但为什么 WPF 一直检测到更改,以便它不断检查CanExecute

4

3 回答 3

13

在你的CanExecuteDelegate你有钩到CommandManager.RequerySuggested

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

每当命令管理器检测到命令源的更改时,都会引发 CommandManager.RequerySuggested 事件,范围从Keyboard.KeyUpEventMouse.ClickEvent等。

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

如果您想掌握提高 CanExecute 的控制权,可以使用PRISM 提供的委托命令。CanExecute仅当您显式调用RaiseCanExecuteChanged()委托命令公开的方法时,才会调用委托。

结合评论来回答

由于 CommandManager RequerySuggested 事件在窗口失去焦点和窗口的激活属性更改时被调用,因此每次转向 VS 时都会出现断点。这就是为什么当您移动到 ​​VS 时,您会注意到断点时不时地命中,因为焦点从 WPF 窗口移动到 Visual Studio。

于 2013-09-23T18:02:35.477 回答
3

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

通常,每当绑定更新或发生某些控制事件时,您都会看到CanExecute被调用(例如,当文本框的文本被突出显示时,CanExecute内置CutCopy命令的绑定到MouseUp事件)。

于 2013-09-23T18:01:03.863 回答
0

可能由于未知原因,UI 可能会更新(测量、排列,然后是渲染调用)。如果您在 can execute 方法上设置了断点,它将再次发生。也就是说你不能通过这个断点,每次你做F5,这个断点都会再次出现。

要进行调查,您应该将日志记录/输出语句放在您可以执行的方法中,以及它被调用的次数和时间。

于 2013-09-23T16:04:46.277 回答