将IsDirty与ICommands一起使用

时间:2010-07-09 20:34:42

标签: wpf mvvm relaycommand

我试图在对象编辑期间使用IsDirty标志来控制CanExecute和Navigational控件。

问题是,为了使其工作,我认为我必须使用onPropertyChanged作为我的IsDirty方法,以便我的控件获得更改通知。(我希望在我的对象IsDirty时禁用一些控件)不幸的是我得到一个令人讨厌的stackoverflow因为它旋转成一个可怕的IsDirty循环......呵呵..

有没有人能够得到与此相似的东西?我正在做的就是在我的OnPropertyChanged方法中将IsDirty设置为true。然后在我的canExecute方法中,我看到它是否设置为true,但是然后在我的控件上我需要Databind到它...这导致所有问题。

有谁知道如何实现这样的东西?

这是我的解决方案

::在ViewModelBase

Private _isdirty As Boolean = False
        Protected Property IsDirty As Boolean
            Get
                Return _isdirty
            End Get
            Set(ByVal value As Boolean)
                If _isdirty = Not value Then
                    _isdirty = value
                    If _isdirty = True Then
                        DisableNavigation()
                    Else
                        EnableNavigation()
                    End If
                End If
            End Set
        End Property

Private _haschanges As Boolean
        Public Property HasChanges As Boolean
            Get
                Return _haschanges
            End Get
            Set(ByVal value As Boolean)
                If value = Not _haschanges Then
                    _haschanges = value
                    OnPropertyChanged("HasChanges")
                End If
            End Set
        End Property



Protected Sub EnableNavigation()
            'Keep from firing multiple onPropertyChanged events
            If HasChanges = True Then
                HasChanges = False
            End If

            GetEvent(Of DisableNavigationEvent).Publish(False)

        End Sub

        Protected Sub DisableNavigation()
            'Keep from firing multiple onPropertyChanged events
            If HasChanges = False Then
                HasChanges = True
            End If
            GetEvent(Of DisableNavigationEvent).Publish(True)

        End Sub

::在从ViewModelBase派生的EditViewModelBase中。

Protected Overrides Sub OnPropertyChanged(ByVal strPropertyName As String)
            MyBase.OnPropertyChanged(strPropertyName)

            If SetsIsDirty(strPropertyName) Then
                If isLoading = False Then

                    IsDirty = True
                Else
                    IsDirty = False

                End If
            End If



        End Sub
        ''' <summary>
        ''' Helps prevent stackoverflows by filtering what gets checked for isDirty
        ''' </summary>
        ''' <param name="str"></param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Protected Function SetsIsDirty(ByVal str As String) As Boolean

            If str = "CurrentVisualState" Then Return False
            If str = "TabsEnabled" Then Return False
            If str = "IsLoading" Then Return False
            If str = "EnableOfficeSelection" Then Return False

            Return True

        End Function

::在我的viewModel

Public ReadOnly Property SaveCommand() As ICommand
            Get
                If _cmdSave Is Nothing Then
                    _cmdSave = New RelayCommand(Of DoctorOffice)(AddressOf SaveExecute, Function() CanSaveExecute())
                End If
                Return _cmdSave
            End Get
        End Property

Private Function CanSaveExecute() As Boolean
            'if the object is dirty you want to be able to save it.
            Return IsDirty

        End Function

        Private Sub SaveExecute(ByVal param As DoctorOffice)
            BeginWait()
            GetService(Of Services.IDoctorOfficesService).Update(SelectedDoctorOffice, False)
            EndWait()

        End Sub

3 个答案:

答案 0 :(得分:1)

避免堆栈溢出的最简单方法是对IsDirty属性setter的一个guard子句:

public bool IsDirty
{
    get { return _isDirty; }
    set
    {
        if (_isDirty == value)
            return;
        _isDirty = value;
        NotifyPropertyChanged("IsDirty");
    }
}

不幸的是,如果你尝试设置IsDirty = false,你仍然会遇到问题,因为它会被PropertyChanged方法重置为true。为避免这种情况,您应检查该方法中的属性名称,如果更改的属性名称为“IsDirty”,则应跳过IsDirty的设置。

答案 1 :(得分:0)

您无需通知IsDirty已更改。只需使它成为一个普通的属性或字段,它应该工作正常(并且没有无限循环)。

这假设您正在使用RelayCommand,每个人似乎都在MSDN杂志的Josh Smith's article中使用(有充分理由)。

答案 2 :(得分:0)

让你的CanExecute谓词ICommand包含IsDirty属性

e.g。

public class MyViewModel
{
  public CanSave { get { return IsDirty;}}
  public void Save(object parameter)
  {
    //Do stuff here
  }

  public ICommand SaveCommand = new RelayCommand(() => this.Save,() => this.CanSave);
}

或者如果CanSave仅引用IsDirty,您可以将ICommand设置为:

public ICommand SaveCommand = new RelayCommand(() => this.Save,() => this.IsDirty);

只要RelayCommandCommandManager.RequerySuggested事件使用CanExecuteChanged,只要ViewModel中的任何绑定值发生更改,CanSave谓词就会被重新获取。

这是一个重点,因为没有CommandManager.RequerySuggested WPF将不知道更新UI。这可能会变得昂贵,因为每次在viewmodel中更改任何值时,所有RelayCommand都会被重新获得。但是只要您的CanExecute谓词是一个简单的计算,它就可以忽略不计,即如果您在CanExecute谓词中调用数据库或Web服务,则会出现一些严重的性能问题:)