0

オブジェクトの編集中にIsDirtyフラグを使用してCanExecuteコントロールとNavigationコントロールを制御しようとしています。

問題は、これが機能するために、IsDirtyメソッドにonPropertyChangedを使用して、コントロールが変更通知を受け取る必要があると思うことです(オブジェクトIsDirtyのときに一部のコントロールを無効にしたい)残念ながら、スタックオーバーフローが発生するため、厄介です。 IsDirtyの恐ろしいループにスパイラル...hehe..

誰かがこれに似たものを機能させることができましたか?私がしているのは、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
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が変更されたことを通知する必要はありません。プレーンなプロパティまたはフィールドにするだけで、正常に機能するはずです(無限ループは発生しません)。

これは、MSDNMagazineのJoshSmithの記事から(正当な理由で)誰もが使用していると思われるRelayCommandを使用していることを前提としています。

于 2010-07-10T04:47:11.980 に答える
0

ICommand に IsDirty プロパティを含めるための CanExecute 述語を用意するだけです

例えば

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 に答える