2013-10-29 31 views
3

我正在嘗試MVVM模式基本級別,並在ICommand CanExecute發生了變化。我已經XAML綁定,如下所示:ICommand CanExecuteChanged沒有更新

<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的實現。

謝謝。

更新後的中繼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 

大部分的代碼是一個副本,但我明白下面的註釋工作的。

+1

嘗試將MVVM Light的'RelayCommand'與Microsoft Prism的'DelegateCommand'進行比較。當一個屬性發生變化時,'RelayCommand'會自動增加'CanExecuteChanged',而'DelegateCommand'則不會自動提升 – Rachel

回答

4

您必須在您的ICommand實現中使用某種方法,如觸發事件CanExecuteChangedRiseCanExecuteChanged。然後每當列表中選定的項目發生更改時,在您的視圖模型中,您可以從所需的命令執行RaiseCanExecuteChanged。我建議你使用任何通用命令,比如GalaSoft MVVMLite庫的RelayCommandDelegateCommand的任何實現。 希望這有助於...

+1

通過在繼電器命令上進行全面的搜索或MSDN搜索之後,它和ICOMMAND的核心區別就是發現raise事件被迫重新查詢未決狀態,並強制執行的代碼是「CommandManager.InvalidateRequerySuggested()」。謝謝。我現在已經對我的代碼進行了更改,並且可以更好地使用MVVM路徑。 – surpavan

6

正如RaulOtaño指出的那樣,您可以提高CanExecuteChanged。但是,並不是所有的MVVM框架都提供了一個方法RaiseCanExecuteChanged。還值得注意的是,必須在UI線程上調用實際事件CanExecuteChanged。所以,如果你希望從你的模型中某個線程的回調,你需要調用它回到UI線程,就像這樣:

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

我非常建議不要打電話CommandManager.InvalidateRequerySuggested(),因爲雖然這個工程功能,對於小型應用程序來說是可以的,它是不加區分的,並且可能會重新查詢每個命令!在一個有很多命令的大系統中,這可能非常慢!

+0

當然,根據你的建議,我通過評論InvalidateRequerySuggested的代碼嘗試過,有趣的是它仍然有效。附上我更新的ICommand代碼。謝謝。 – surpavan

+0

WPF將對查詢命令狀態使用任何本地更改,例如,如果單擊按鈕或單擊窗體,或者如果由於按鈕單擊而導致發生命令狀態更改,則此更改將會顯示無需手動刺激命令。但是,如果你的命令是異步完成的(想象後臺工作者或者帶有oncompleted事件的類似事件),那麼執行完成的句柄需要更新該命令依賴的適當數據,然後在命令中調用RaiseCanExecuteChanged事件。希望這可以幫助! –

+0

這表明一些光。謝謝你的時間。 – surpavan