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

tet*_*ete 6 vb.net wpf mvvm icommand

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

这是我对VB.NET的实现,我最初是从C#版本获得的.我注意到我需要对移植的代码进行一些更改才能进行编译.难道它是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
Run Code Online (Sandbox Code Playgroud)

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

MyCommand = New CommandBase(AddressOf CommandExec, AddressOf CanExecuteExec)
Run Code Online (Sandbox Code Playgroud)

CanExecuteExec当然有这样的签名:

Private Function CanExecuteExec(obj As Object) As Boolean
Run Code Online (Sandbox Code Playgroud)

就像我提到的那样,它CanExecuteExec一直在被召唤.我想这是低效的,想象一下我有数百个Command对象,而且大多数对象大多数CanExecute时间都不会被改变.

更新:

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

Roh*_*ats 12

在你的CanExecuteDelegate钩子里CommandManager.RequerySuggested.

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

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

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

如果你想拿出控件来提升CanExecute,你可以使用PRISM提供的Delegate Command.CanExecute只有在显式调用RaiseCanExecuteChanged()Delegate Command公开的方法时才会调用delegate .

纳入评论以回答

由于CommandManager RequerySuggested事件在窗口丢失焦点和窗口激活属性改变时被调用,因此每次转向VS时都会出现断点.这就是为什么你注意到断点正在随时移动到VS,因为焦点从WPF窗口移动到Visual Studio.