使用ViewModel和MVVM设计模式以及UI更新拒绝Changes()
本文关键字:拒绝 更新 Changes UI ViewModel MVVM 设计模式 使用 | 更新日期: 2023-09-27 18:27:28
我在DomainContext.RejectChanges()中遇到问题,并在UI中反映回滚。这是我的场景。
- 我生成了一个模型(实体),用于RIA服务(我称之为Foo)
- 我有一个ViewModel,它封装并扩展了Foo(我称之为FooViewModel)
- 我有一个View,它使用Binding来显示和更新数据,使用FooViewModel
- 我有一个"外部"ViewModel,它包含FooViewModels的ObservableCollection
- "外部"视图有一个绑定到ObservableCollection的列表框
因此,本质上在一个屏幕上有一个FooViewModels的列表框。。。当您选择一个项目时,会显示一个子窗口来编辑特定的FooViewModel。FooViewModel同时为列表框和子窗口提供服务。
编辑效果很好。子窗口中的更改会立即反映在列表框中,因为当视图模型属性更新时,我正在调用RaisePropertyChanged()。
但是,如果我执行DomainContext.RejectChanges()…底层实体将回滚(所有更改按预期恢复)。。。但是FooViewModel没有意识到发生了此更改,因此UI没有更新。如果我在第一个屏幕的列表框中重新选择项目,子窗口将显示回滚的更改(这正是我想要的)。但是列表框仍然没有更新。
当我拒绝更改时,如果我对更改的字段进行kludgeaRaisePropertyChanged()。。。UI列表框确实会更新。
当基础实体被拒绝时,我如何更新UI??在不跟踪视图模型的哪些属性被回滚的情况下,我该如何做到这一点?必须有一个简单的方法来实现这一点,我只是错过了。
您可以尝试在基础实体Foo
上使用PropertyChanged事件来触发对FooViewModel
属性的RaisePropertyChanged传递。
所以做一些假设(所以这个代码有意义):
-
您的
FooViewModel
中有一个私有变量private Foo _foo;
private DomainContext _context;
-
您的
FooViewModel
上有一个方法,它正在域上下文中调用RejectChanges()
。
像这样:
public void RejectChanges()
{
_context.RejectChanges();
}
- 我们有一个在
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
{
...
}
这只是一个想法,但可能不是一个完美的解决方案。