使用ViewModel和MVVM设计模式和UI更新的RejectChanges()

时间:2012-03-13 01:56:22

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

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

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

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

    编辑工作正常。子窗口的更改立即反映在列表框中,因为我在更新viewmodel属性时调用 RaisePropertyChanged()

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

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

    当基础实体被拒绝时,如何让UI更新?如何在不跟踪视图模型的哪些属性回滚的情况下执行此操作?必须有一种简单的方法来实现这一点,我才会失踪。

2 个答案:

答案 0 :(得分:2)

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

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

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

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

  3. 像这样:

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

      像这样:

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

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

      当您致电RejectChanges()时,这会在DomainContextEntityContainer,然后到该容器中的每个EntitySet,然后到每个Entity。集合。

      一旦存在(并且在EntitySet中),它会重新应用原始值(如果有的话),删除实体(如果已添加),或者添加实体(如果已删除)。如果值发生了变化,则会将它们应用回属性。

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

      注意:我实际上没有测试过这个。如果情况并非如此,那么这一切都不起作用:P

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

      所以我们的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方法。

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

      然后我们取消了事件处理程序。

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

答案 1 :(得分:0)

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

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

如何通过每个属性的RaisePropertyChangeOnAll()列表简单地实现RaisePropertyChange("..."),或者通过反射(如果Silverlight权限允许,不太确定)通过添加属性来实现RaisePropertyChanged在您想要筹集的每个房产上。找到所有使用它标记的属性,并在MemberInfo.Name值上调用[Raiseable] public string SomeValue { ... }

{{1}}

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