【问题标题】:ICommand CanExecuteChanged not updatingICommand CanExecuteChanged 未更新
【发布时间】:2013-10-29 11:51:51
【问题描述】:

我正在尝试 MVVM 模式基本级别,但对 ICommand CanExecute 更改感到震惊。我有如下 XAML 绑定:

    <ListBox ItemsSource="{Binding Contact.Addresses}"  x:Name="AddressCollections" Height="152" SelectedValue="{Binding SelectedAddress}"
             DockPanel.Dock="Top" />
    <Button Content="Add" Command="{Binding AddAddressCommand}" DockPanel.Dock="Top" />
    <Button Content="Remove" Command="{Binding DeleteAddressCommand}" DockPanel.Dock="Bottom" />

命令:

Public Class DeleteCommand
Implements ICommand

Private method As Object
Private methodname As String

Public Sub New(ByVal Controlname As String, ByVal mee As Object)
    methodname = Controlname
    method = mee
End Sub

Public Function CanExecute(parameter As Object) As Boolean Implements ICommand.CanExecute
    Select Case methodname
        Case "Address"
            Return TryCast(method, ModelView.Contacts.ContactMV).CanDeleteAddress()
        Case "Numbers"
            Return TryCast(method, ModelView.Contacts.ContactMV).CanDeleteNumbers
        Case Else : Return False
    End Select
End Function

Public Event CanExecuteChanged(sender As Object, e As EventArgs) Implements ICommand.CanExecuteChanged

Public Sub Execute(parameter As Object) Implements ICommand.Execute
    Select Case methodname
        Case "Address"
            TryCast(method, ModelView.Contacts.ContactMV).DeleteAddress()
        Case "Numbers"
            TryCast(method, ModelView.Contacts.ContactMV).DeleteNumbers()
        Case Else

    End Select
End Sub
End Class

我的模型视图:

Public Class ContactMV

Property Contact As Model.Contacts.ContactMod
Property AddAddressCommand As New Commands.AddCommand("Address", Me)
Property DeleteAddressCommand As New Commands.DeleteCommand("Address", Me)
Property SelectedAddress As Model.Contacts.AddressModel
Public Sub AddAddress()
    If Contact.Addresses.Count = 0 Then
        Contact.Addresses.Add(New Model.Contacts.AddressModel(Contact.Primary.ID, True))
    Else
        Contact.Addresses.Add(New Model.Contacts.AddressModel(Contact.Primary.ID, False))
    End If

End Sub
Public Sub DeleteAddress()
    If IsNothing(SelectedAddress) = False Then
        Try
            Contact.Addresses.Remove(SelectedAddress)
        Catch ex As Exception
            MsgBox("Address not found")
        End Try
    End If
End Sub
Public Function CanDeleteAddress()
    If IsNothing(SelectedAddress) Then
        Return False
    Else
        Return Contact.Addresses.Contains(SelectedAddress)
    End If
End Function
End Class

问题是 Canexecutechanged 仅在启动时触发,我实际上希望仅在选择列表框中的某些内容时启用删除按钮,并且我想通过 MVVM - ICommand 绑定方法完成它。您能否解释一下我哪里出错或错过了对 ICommand 实现的理解。

谢谢。

更新了我使用的 Relay iCommand 代码:

    Public Class RelayCommand
        Implements ICommand
        ''' <summary>
        ''' A command whose sole purpose is to relay its functionality to other objects by invoking delegates. The default return value for the CanExecute method is 'true'.
        ''' </summary>
        ''' <remarks></remarks>

#Region "Declarations"
        Private ReadOnly _CanExecute As Func(Of Boolean)
        Private ReadOnly _Execute As Action
#End Region

#Region "Constructors"
        Public Sub New(ByVal execute As Action)
            Me.New(execute, Nothing)
        End Sub

        Public Sub New(ByVal execute As Action, ByVal canExecute As Func(Of Boolean))
            If execute Is Nothing Then
                Throw New ArgumentNullException("execute")
            End If
            _Execute = execute
            _CanExecute = canExecute
        End Sub
#End Region

#Region "ICommand"
        Public Custom Event CanExecuteChanged As EventHandler Implements System.Windows.Input.ICommand.CanExecuteChanged

            AddHandler(ByVal value As EventHandler)
                If _CanExecute IsNot Nothing Then
                    AddHandler CommandManager.RequerySuggested, value
                End If
            End AddHandler
            RemoveHandler(ByVal value As EventHandler)

                If _CanExecute IsNot Nothing Then
                    RemoveHandler CommandManager.RequerySuggested, value
                End If
            End RemoveHandler

            RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)
                'This is the RaiseEvent block
                'CommandManager.InvalidateRequerySuggested()
            End RaiseEvent
        End Event

        Public Function CanExecute(ByVal parameter As Object) As Boolean Implements System.Windows.Input.ICommand.CanExecute
            If _CanExecute Is Nothing Then
                Return True
            Else
                Return _CanExecute.Invoke
            End If
        End Function

        Public Sub Execute(ByVal parameter As Object) Implements System.Windows.Input.ICommand.Execute
            _Execute.Invoke()
        End Sub
#End Region
    End Class

大部分代码是副本,但我通过下面的 cmets 理解了工作。

【问题讨论】:

  • 尝试比较 MVVM Light 的 RelayCommand 和 Microsoft Prism 的 DelegateCommandRelayCommand 将在属性更改时自动提高 CanExecuteChanged,而 DelegateCommand 不会

标签: wpf vb.net mvvm icommand


【解决方案1】:

正如 Raul Otaño 所指出的,您可以提出 CanExecuteChanged。但是,并非所有 MVVM 框架都提供RaiseCanExecuteChanged 方法。还值得注意的是,必须在 UI 线程上调用实际事件 CanExecuteChanged。因此,如果您期望模型中某个线程的回调,则需要将其调用回 UI 线程,如下所示:

    public void RaiseCanExecuteChanged()
    {
        if (CanExecuteChanged != null)
        {
            Application.Current.Dispatcher.Invoke((Action)(() => { CanExecuteChanged(this, EventArgs.Empty); }));
        }
    }

我强烈建议不要调用CommandManager.InvalidateRequerySuggested(),因为尽管这在功能上有效,并且适用于小型应用程序,但它是不分青红皂白的,并且可能会重新查询每个命令!在具有大量命令的大型系统中,这可能非常非常慢!

【讨论】:

  • 当然,根据您的建议,我尝试通过评论 InvalidateRequerySuggested 的代码,有趣的是它仍然有效。附上我更新的 ICommand 代码。谢谢。
  • WPF 将使用任何本地更改来重新查询命令状态,例如,如果单击按钮或单击表单,或者命令状态的更改是由于按钮而发生的例如,单击此更改将显示而无需手动触发命令。但是,如果您的命令是异步完成的(想想后台工作人员或类似的 oncompleted 事件),oncompleted 句柄需要更新命令所依赖的适当数据,然后在命令上调用 RaiseCanExecuteChanged 事件。希望这会有所帮助!
【解决方案2】:

您的 ICommand 实现中必须有一些方法,例如 RaiseCanExecuteChanged,它会触发事件 CanExecuteChanged。然后,每次列表中的选定项目发生更改时,在您的视图模型中,您从所需的命令执行 RaiseCanExecuteChanged。无论如何,我建议您使用任何通用命令,例如 GalaSoft MVVMLite 库的RelayCommand,或DelegateCommand 的任何实现。 希望这会有所帮助...

【讨论】:

  • 在对relay命令进行彻底的谷歌搜索或MSDN搜索后,它与ICOMMAND之间的核心区别发现引发事件被强制重新查询可以执行状态,并且强制代码是“CommandManager.InvalidateRequerySuggested()”。谢谢你。我现在已经对我的代码进行了更改,并且可以更好地使用 MVVM 路径。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-08
  • 2014-06-26
  • 2016-04-23
  • 2011-05-30
相关资源
最近更新 更多