wpf ICommand CanExecuteChanged 未更新

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19657463/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 09:58:26  来源:igfitidea点击:

ICommand CanExecuteChanged not updating

wpfvb.netmvvmicommand

提问by surpavan

I am trying for MVVM pattern basic level and got struck at ICommand CanExecute changed. I have XAML binding as follows:

我正在尝试 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" />

Commands:

命令:

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

My ModelView:

我的模型视图:

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

The problem is that the Canexecutechanged is firing only at start, I actually want to get the delete button enabled only when something in the listbox is selected, and I want to get it done by MVVM - ICommand binding method. Could you please explain where i went wrong or miss understood the ICommand implementation.

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

Thank you.

谢谢你。

Updated Relay iCommand code I use:

我使用的更新的中继 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

Most of the code is a copy, but I understood the working by below comments.

大多数代码都是副本,但我通过下面的评论理解了工作原理。

回答by Immortal Blue

As Raul Ota?o has pointed out, you can raise the CanExecuteChanged. However, not all MVVM frameworks provide a RaiseCanExecuteChangedmethod. It's also worth noting that the actual event CanExecuteChangedmust be called on the UI thread. So, if you're expecting a callback from some thread in your model, you need to invoke it back to the UI thread, like this:

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

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

I would very much recommend against calling CommandManager.InvalidateRequerySuggested()because although this works functionally, and is ok for small applications, it is indiscriminate and will potentially re-query every command! In a large system with lots of commands, this can be very very slow!

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

回答by Raúl Ota?o

You must have in your ICommand implementation some method like RiseCanExecuteChangedthat fires the event CanExecuteChanged. Then every time that the selected item in the list changed, in your view model you execute the RaiseCanExecuteChangedfrom the command you want. Any way I suggest you to use any generic command, like the RelayCommandof GalaSoft MVVMLite library, or any implementation of DelegateCommand. Hope this helps...

您必须在您的 ICommand 实现中使用某种方法RiseCanExecuteChanged来触发事件CanExecuteChanged。然后每次列表中的选定项发生更改时,在您的视图模型中,您都可以RaiseCanExecuteChanged从所需的命令中执行。我建议您以任何方式使用任何通用命令,例如RelayCommandGalaSoft MVVMLite 库的,或DelegateCommand. 希望这可以帮助...