使用ViewModel和MVVM设计模式以及UI更新拒绝Changes()

本文关键字:拒绝 更新 Changes UI ViewModel MVVM 设计模式 使用 | 更新日期: 2023-09-27 18:27:28

我在DomainContext.RejectChanges()中遇到问题,并在UI中反映回滚。这是我的场景。

  1. 我生成了一个模型(实体),用于RIA服务(我称之为Foo
  2. 我有一个ViewModel,它封装并扩展了Foo(我称之为FooViewModel
  3. 我有一个View,它使用Binding来显示和更新数据,使用FooViewModel
  4. 我有一个"外部"ViewModel,它包含FooViewModelsObservableCollection
  5. "外部"视图有一个绑定到ObservableCollection的列表框

因此,本质上在一个屏幕上有一个FooViewModels的列表框。。。当您选择一个项目时,会显示一个子窗口来编辑特定的FooViewModelFooViewModel同时为列表框和子窗口提供服务。

编辑效果很好。子窗口中的更改会立即反映在列表框中,因为当视图模型属性更新时,我正在调用RaisePropertyChanged()

但是,如果我执行DomainContext.RejectChanges()…底层实体将回滚(所有更改按预期恢复)。。。但是FooViewModel没有意识到发生了此更改,因此UI没有更新。如果我在第一个屏幕的列表框中重新选择项目,子窗口将显示回滚的更改(这正是我想要的)。但是列表框仍然没有更新。

当我拒绝更改时,如果我对更改的字段进行kludgeaRaisePropertyChanged()。。。UI列表框确实会更新。

当基础实体被拒绝时,我如何更新UI??在不跟踪视图模型的哪些属性被回滚的情况下,我该如何做到这一点?必须有一个简单的方法来实现这一点,我只是错过了。

使用ViewModel和MVVM设计模式以及UI更新拒绝Changes()

您可以尝试在基础实体Foo上使用PropertyChanged事件来触发对FooViewModel属性的RaisePropertyChanged传递。

所以做一些假设(所以这个代码有意义):

  1. 您的FooViewModel中有一个私有变量
    private Foo _foo;
    private DomainContext _context;

  2. 您的FooViewModel上有一个方法,它正在域上下文中调用RejectChanges()

像这样:

public void RejectChanges()
{
    _context.RejectChanges();
}
  1. 我们有一个在FooViewModel上引发PropertyChanged事件的方法

像这样:

private void RaisePropertyChanged(string propertyName)
{
    var handler = PropertyChanged;
    if(handler != null)
        handler(this, new PropertyChangedEventArgs(propertyName);
}

好的,现在我们已经确定了,让我们看看当您在域上下文上调用RejectChanges()时会发生什么。

当您调用RejectChanges()时,它会通过DomainContext向下冒泡到它的EntityContainer,然后到该容器中的每个EntitySet,然后到集合中的每一个Entity

一旦到达(以及在EntitySet中),它将重新应用原始值(如果有),如果添加了实体则删除实体,如果删除了实体则添加实体。如果值发生了更改,则会将其应用回属性。

因此,理论上,实体属性中生成的所有RaisePropertyChanged()都应该被触发。

注意:我还没有真正测试过这个。如果不是这样,那么这些都不起作用:P

因此,我们可以挂接到Foo实体的PropertyChanged事件,并在我们的FooViewModel上引发PropertyChanged事件。

所以我们的RejectChanges()方法可能看起来像这样:

    public void RejectChanges()
    {
        Func<object, PropertyChangedEventArgs> handler = (sender, e) =>
            {
                RaisePropertyChanged(e.PropertyName);
            };
        _foo.PropertyChanged += handler;
        _context.RejectChanges();
        _foo.PropertyChanged -= handler;
    }

因此,我们将一个事件处理程序连接到我们的Foo实体,该实体调用FooViewModel.RaisePropertyChanged方法,该方法的属性名称在Foo实体上正在更改。

然后我们拒绝更改(这会触发属性更改),

然后我们解除挂起事件处理程序。

很冗长,但我希望这能有所帮助:)

我假设对DomainContext.RejectChanges()的调用发生在ViewModel中,因为您可能将其绑定到从父ViewModel调用的某个命令或方法。由于与数据的所有绑定都是在ViewModel属性上完成的,因此当您在这些属性之外直接操作模型时,必须在这些属性上引发属性更改事件。你可能已经在做了。

public void RejectChanges()
{
     DomainContext.RejectChanges();
     RaisePropertyChangeOnAll();
}

实现RaisePropertyChangeOnAll()的方法可以简单地为每个属性列出RaisePropertyChange("..."),也可以通过反射来实现(如果Silverlight权限允许,则不太确定),在要引发的每个属性上添加一个Attribute。查找所有用它标记的属性,并对MemberInfo.Name值调用RaisePropertyChanged

[Raiseable]
public string SomeValue
{
   ...
}

这只是一个想法,但可能不是一个完美的解决方案。