【问题标题】: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,其中包含 FooViewModelsObservableCollection
  5. “外部”View 有一个绑定到 ObservableCollection 的列表框

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

编辑工作得很好。子窗口中的更改立即反映在列表框中,因为我在更新视图模型属性时调用 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
相关资源
最近更新 更多