从ViewModel通知用户并取消导航

时间:2011-09-02 15:07:45

标签: c# wpf mvvm mvvm-light

我有一个包含项目列表的视图,TextBox和保存按钮。 TextBox绑定到列表中当前所选项的属性。列表的DataSource绑定到ViewModel中的ObservableCollection<T> 现在,当用户选择列表中的另一个项目并且尚未将其更改保存到TextBox时,应该询问他是否要放弃他所做的更改。列表中的所选项目只有在回答“是”时才会更改 我遇到的问题是:
我需要在ViewModel中实现对更改的检查,但是我不知道ViewModel何时没有收到通知,所选项目的更改时间。


我提出了以下方法,但它看起来并不干净:
列表中有一个事件SelectedItemsChanging。我可以使用EventToCommand行为并将CancelEventArgs作为参数传递给命令。在命令中,我检查项目是否已更改,如果是,我可以使用messenger发送View侦听的消息。然后,View将向用户显示确认对话框,并将结果返回给ViewModel 以某种方式。如果用户不想丢弃他的更改,ViewModel会将事件args的Cancel属性设置为true
这看起来并不干净,因为它将这个简单的功能分开并将其涂抹在三个文件上,这使得它很难理解。
对于这样的场景,是否有最佳实践?

3 个答案:

答案 0 :(得分:1)

我会对模型类进行Dirty检查。示例如下:

Public m_dirtyFields As New Dictionary(Of String, String)

Private Sub AddDirtyField(ByVal ColName As String, ByVal OrigValue As String)
    If Not m_dirtyFields.ContainsKey(ColName) Then
        m_dirtyFields.Add(ColName, OrigValue)
        OnPropertyChanged("IsDirty")
    End If
End Sub
Private Sub RemoveDirtyField(ByVal ColName As String)
    If m_dirtyFields.ContainsKey(ColName) Then
        m_dirtyFields.Remove(ColName)
    End If
    OnPropertyChanged("IsDirty")
End Sub

Private Sub OnAddress1Changing(ByVal value As String)
    If Not m_dirtyFields.ContainsKey("Address1") Then
        AddDirtyField("Address1", Address1)
    Else
        If m_dirtyFields("Address1") = value Then RemoveDirtyField("Address1")
    End If
End Sub

Public ReadOnly Property IsDirty
    Get
        If _Initialized = False Then
            m_dirtyFields.Clear()
            _Initialized = True
        End If

        If m_dirtyFields.Count > 0 Then
            Return True
        Else : Return False
        End If
    End Get
End Property

上面的示例检查属性值,根据原始值的相似性将它们添加到字典中,并根据字典中的项返回Dirty。

在ViewModel中,您只需检查MyObject.IsDirty,如果它已更改,则弹出一个消息框,要求用户保存(或不保存)。

此外,您可以在记录脏时禁用列表框(因此用户无法更改记录),但在ViewModel上有一个属性,该属性通告了SelectedItem的Dirty属性。

答案 1 :(得分:0)

经过一些研究,我偶然发现了PRISM使用的“交互请求”“模式”。这基本上和我在答案中已经列出的解决方案相同,只是稍微复杂一点。这就是我现在正在使用的。

它的工作原理如下:

  1. ViewModel具有我的自定义界面IInteractionRequest<TInteractionData>的属性。此界面仅包含事件Raised
  2. 在视图中,行为绑定到此属性。

    <i:Interaction.Behaviors>
        <Interaction:NotificationMessageBoxBehavior
             SourceObject="{Binding NotificationRequest}" />
    </i:Interaction.Behaviors>
    

    绑定行为(在示例中为NotificationMessageBoxBehavior)控制交互请求的处理方式。例如,在Windows应用程序中,这只是调用MessageBox.Show,但可能有一个在Silverlight应用程序中有效的替代实现。
    这些行为都源自一个公共基类,该基类派生自Behavior<FrameworkElement>,并具有类型为SourceObject的依赖项属性IInteractionRequest<TInteractionData>。这就是行为和交互请求汇集在一起​​的方式:行为订阅Raised的{​​{1}}事件,并在事件发生时执行交互请求。

  3. 请求中使用的交互数据的具体实现可以包含在请求完成后调用的回调。此回调可以有一个参数。像这样,交互请求的结果可以流回ViewModel。

答案 2 :(得分:0)