【问题标题】:RejectChanges() with ViewModel and MVVM design pattern and UI updateRejectChanges() 与 ViewModel 和 MVVM 设计模式和 UI 更新
【发布时间】:2012-03-29 11:03:35
【问题描述】:

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

  1. 我生成了一个 模型(实体)用于 RIA 服务(我将其称为 Foo)
  2. 我有一个 ViewModel,它包装了 Foo 并对其进行了扩展(我将其称为 FooViewModel)
  3. 我有一个 View,它使用 Binding 来显示和使用 FooViewModel 更新数据
  4. 我有一个“外部”ViewModel,其中包含 FooViewModels 的 ObservableCollection
  5. “外部”View 有一个绑定到 ObservableCollection 的列表框

所以基本上在一个屏幕上有一个 FooViewModels 列表框...当您选择一个项目时,会显示一个子窗口来编辑该特定的 FooViewModel。 FooViewModel 为列表框和子窗口提供服务。

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

但是,如果我执行 DomainContext.RejectChanges()...基础实体会回滚(所有更改都按预期还原)...但是 FooViewModel不知道此更改已发生,因此 UI 未更新。如果我在第一个屏幕上重新选择列表框中的项目,子窗口将显示回滚更改(这是我想要的)。但是列表框仍然没有更新。

当我拒绝更改时,如果我为我更改的字段kludge RaiseProperyChanged()...UI 列表框会更新。

当底层实体被拒绝时,如何让 UI 更新?在不跟踪视图模型的哪些属性被回滚的情况下,我该怎么做?必须有一种简单的方法来完成我只是想念的。

【问题讨论】:

  • 您的 FooViewModel 属性是否直接映射到 Foo 实体属性?例如。相同的名称,相同的类型?
  • 是的。底层 Foo.MyProperty 公开为 FooViewModel.MyProperty。 FooViewModel 上确实有其他提供视图的属性....但这些都是“额外的”。

标签: c# silverlight mvvm wcf-ria-services silverlight-5.0


【解决方案1】:

您可以尝试使用基础实体 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 实体,它使用 Foo 实体上正在更改的属性名称调用 FooViewModel.RaisePropertyChanged 方法。

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

然后我们解开事件处理程序。

相当冗长,但我希望这会有所帮助:)

【讨论】:

  • 啊...你让我到​​了那里!关键是实体会引发 PropteryChanged 事件。我通过在 FooViewModel 的构造函数中添加一个事件处理程序来简化,如下所示: _Foo.PropertyChanged += new PropertyChangedEventHandler(_Foo_PropertyChanged);然后: void _Foo_PropertyChanged(object sender, PropertyChangedEventArgs e) { RaisePropertyChanged(e.PropertyName); } 这会使事件冒泡。将导致 UI 的额外“刷新”,但它似乎不会导致问题并且不引人注意。我将不得不密切关注它。谢谢!
  • ...只是将此评论发送给您,让您知道您解决了我的问题。谢谢。
  • 我很高兴这行得通。在我们的代码中,我们只是在 ViewModel 中公开 RIA 服务生成的实体并直接绑定它们。它解决了这类问题:)
  • 我开始考虑这样做。似乎在关注点分离的影响最小的情况下,这会容易得多。
  • 我们发现通过 ViewModel 再次公开所有属性更容易。显着减少编码和生成的实体都为 RaisePropertyChanged & Validation 设置。
【解决方案2】:

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

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

您如何实现RaisePropertyChangeOnAll() 可以简单地使用每个属性的RaisePropertyChange("...") 列表来完成,或者您可以通过在每个属性上添加一个属性来通过反射(如果 Silverlight 权限允许,不太确定)来完成想提高。找到所有带有它标记的属性,并在 MemberInfo.Name 值上调用 RaisePropertyChanged。

[Raiseable]
public string SomeValue
{
   ...
}

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

【讨论】:

  • 感谢您的回复。我想过这样做,但我认为会有更好的方法来处理这种情况。我将推迟等待,看看是否有人有更优雅的解决方案。如果我没有得到,我会把你的标记为正确的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-03
  • 1970-01-01
  • 1970-01-01
  • 2011-07-05
  • 2015-08-24
  • 1970-01-01
相关资源
最近更新 更多