0

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

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

有没有人能够得到类似的东西来工作?我所做的只是在我的 OnPropertyChanged 方法中将 IsDirty 设置为 true。然后在我的 canExecute 方法中,我查看它是否设置为 true,但是在我的控件上,我需要将数据绑定到它……这导致了所有问题。

有谁知道如何实现这样的事情?

这是我的解决方案

:: 在 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

:: 在我的视图模型中

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
4

3 回答 3

1

避免堆栈溢出的最简单方法是为 IsDirty 属性设置器添加一个保护子句:

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

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

于 2010-07-10T03:24:39.570 回答
0

您无需通知 IsDirty 已更改。只需将其设为普通属性或字段,它应该可以正常工作(并且没有无限循环)。

这是假设您使用的 RelayCommand 似乎每个人都在使用(有充分的理由)来自Josh Smith在 MSDN 杂志上的文章。

于 2010-07-10T04:47:11.980 回答
0

只需让您的 CanExecute 谓词为您的 ICommand 包含 IsDirty 属性

例如

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);

只要RelayCommand用于事件,CommandManager.RequerySuggested就会在 ViewModel 中的任何绑定值更改时重新查询谓词。CanExecuteChangedCanSave

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

于 2010-07-12T16:13:21.617 回答