Wpf ICommand CanExecuteChanged未更新

Wpf ICommand CanExecuteChanged未更新,wpf,vb.net,mvvm,icommand,Wpf,Vb.net,Mvvm,Icommand,我正在尝试MVVM模式的基本级别,但在ICommand CanExecute更改时被击中。我有如下XAML绑定: <ListBox ItemsSource="{Binding Contact.Addresses}" x:Name="AddressCollections" Height="152" SelectedValue="{Binding SelectedAddress}" DockPanel.Dock="Top" /> <But

我正在尝试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 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仅在开始时启动,我实际上只想在列表框中的某些内容被选中时启用delete按钮,并且我想通过MVVM-ICommand绑定方法来完成。请您解释一下我哪里出错了,或者没有理解ICommand实现

多谢各位

更新了我使用的继电器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
公共类中继命令
实现ICommand
''' 
''一个命令,其唯一目的是通过调用委托将其功能传递给其他对象。CanExecute方法的默认返回值为“true”。
''' 
''' 
#区域“宣言”
私有只读\u可作为Func(布尔值)执行
私有只读\u作为操作执行
#末端区域
#地区“建设者”
Public Sub New(ByVal作为操作执行)
Me.New(执行,无)
端接头
Public Sub New(ByVal作为操作执行,ByVal可以作为Func执行(布尔值))
如果执行什么都不是,那么
抛出新ArgumentNullException(“执行”)
如果结束
_执行
_CanExecute=CanExecute
端接头
#末端区域
#区域“ICommand”
公共自定义事件CanExecuteChanged,因为EventHandler实现System.Windows.Input.ICommand.CanExecuteChanged
AddHandler(作为EventHandler的ByVal值)
如果执行不是什么,那么
AddHandler CommandManager.RequerySuggested,值
如果结束
EndAddHandler
RemoveHandler(ByVal值作为EventHandler)
如果执行不是什么,那么
RemoveHandler CommandManager.RequerySuggested,值
如果结束
末端移除处理器
RaiseEvent(ByVal发送方作为对象,ByVal e作为System.EventArgs)
“这是RaiseEvent块
'CommandManager.InvalidateRequestSuggested()
端部提升孔
结束事件
公共函数CanExecute(ByVal参数作为对象)作为布尔值实现System.Windows.Input.ICommand.CanExecute
如果不能执行什么,那么
返回真值
其他的
Return\u CanExecute.Invoke
如果结束
端函数
Public Sub Execute(ByVal参数作为对象)实现System.Windows.Input.ICommand.Execute
_Execute.Invoke()
端接头
#末端区域
末级

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

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

正如劳尔·奥塔尼奥所指出的,您可以提高
CanExecuteChanged
。然而,并不是所有的MVVM框架都提供了一个
raisecancecutechanged
方法。还值得注意的是,实际事件
CanExecuteChanged
必须在UI线程上调用。因此,如果您希望从模型中的某个线程调用回调,则需要将其调用回UI线程,如下所示:

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

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

尝试比较MVVM Light的
RelayCommand
与Microsoft Prism的
DelegateCommand
。当属性更改时,
RelayCommand
将自动引发
CanExecuteChanged
,而
DelegateCommand
不会在彻底搜索relay命令或MSDN后,发现它与ICOMMAND之间的核心区别是raise事件被强制重新查询canexecute状态,而要强制执行的代码是“CommandManager.InvalidateRequestSuggested()”“谢谢。我现在已经对我的代码进行了更改,并且可以更好地使用MVVM路径。当然,根据您的建议,我尝试对InvalidateRequestSuggestived的代码进行注释,有趣的是,它仍然在工作。附上我更新的ICommand代码。谢谢。WPF将使用任何本地更改来重新查询命令状态,因此,例如,如果您单击按钮或表单,或者如果命令状态的更改是由于按钮单击而发生的,例如,此更改将显示,而无需手动生成命令。但是,如果您的命令是异步完成的(请考虑后台工作程序或与oncompleted事件类似的情况),oncompleted句柄需要更新该命令所依赖的适当数据,然后对该命令调用RaiseCanExecuteChanged事件。希望这有帮助!
    public void RaiseCanExecuteChanged()
    {
        if (CanExecuteChanged != null)
        {
            Application.Current.Dispatcher.Invoke((Action)(() => { CanExecuteChanged(this, EventArgs.Empty); }));
        }
    }