【问题标题】:A better way to intercept property updates from UI using EF and MVVM使用 EF 和 MVVM 从 UI 拦截属性更新的更好方法
【发布时间】:2012-07-13 09:48:38
【问题描述】:

我有一个具有撤消重做功能的 WPF 应用程序。我使用 EF 与数据库进行交互。

我想知道是否有更好的方法或模式可以在 UI 更改某些属性时得到通知。

这是我目前使用的方法:

  • 让 EF 生成类。例如:

    public partial class Page: IObjectWithChangeTracker, INotifyPropertyChanged
    {
    #region Primitive Properties
    
    [DataMember]
    public string title
    {
        get { return _title; }
        set
        {
            if (_title != value)
            {
                _title = value;
                OnPropertyChanged("title");
            }
        }
    }
    private string _title;
    
    ...
    
  • 然后在类的非生成部分,我为 UI 添加另一个属性。在这种特殊情况下,该属性绑定到 WPF 文本框:

    public partial class Page
    {
    public Page()
    {
        ((INotifyPropertyChanged)this).PropertyChanged += Page_PropertyChanged;
    }
    
    void Page_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        switch (e.PropertyName) {
        case "title":
            OnPropertyChanged("titleUI");
            break;
        }
    }
    
    public string titleUI
    {
        get { return title; }
        set
        {
            if (value == title)
                return;
            string oldValue = title;
            title = value;
            UndoRedo.PushAtomic(
                () => title = oldValue, 
                () => title = value,
                "change page title");
        }
    }
    
    ...
    

我曾经为此目的收听 FrameworkElement.SourceUpdated,但我放弃了这种方法以更紧密地遵循 MVVM 设计模式。

有没有更好或更简单的方法?

编辑: 我不直接将 TextBox 绑定到 title 属性的原因是,我不想在属性被某些东西更新时添加到 undo-redo 堆栈除了文本框。此外,当 EF ObjectContext 实现实体时,值由 setter 分配,我也不想将其添加到 undo-redo 堆栈中。

编辑 2: 对于这个问题,我有点放弃了。如果可以,我会删除它。例如,我无法理解关于我的代码不是真正的 MVVM 的 cmets 与手头的问题有什么关系。无论我是否将实体类与所谓的 ViewModel 类严格分开,我仍然必须找到一种方法来“拦截”来自 UI 的更新。我投票关闭。如果你和我一样觉得这无济于事,请也这样做。

【问题讨论】:

  • 这里似乎是一种奇怪的方法 - 我是否正确,您有混合模型/视图模型?看起来 Page 是您的模型,但您只是通过使用部分将 Page 类扩展为您的 ViewModel?
  • 是的,ViewModel 将是生成的类(即模型)的“扩展”。
  • 好吧,从技术上讲,我不会称它为视图模型——它更像是一个丰富的模型——你不能绑定到 Title 属性有什么原因吗?由于您几乎正在制作一个丰富的模型,您不妨使用它的属性
  • 看到这是我在使用 MVVM 时遇到的问题 - 事实上,您最终会在 VM 中重新实现所有内容。有些框架可以为您提供丰富的(生成的)模型,然后允许您通过 VM 公开它。通过这种方式,您可以绑定到模型上的属性,而不是在视图模型中重新实现它们。在您的情况下,可能值得使用 Unity/Ninject 之类的东西来挂钩属性更改事件 - 这样您就可以将代码注入到生成的代码中,而无需重新实现所有内容。我不喜欢这段代码的样子,它会让我头疼
  • @Charleh:我不得不问:为什么不能将类的文件分离视为模型/视图模型分离?你是根据什么做出这样的肯定的?

标签: c# .net wpf entity-framework mvvm


【解决方案1】:

wpf 文本框具有内置的撤消/重做功能 - 因此,如果您只是将视图模型属性与 mode=twoway 绑定,您的视图模型和视图始终保持同步,并且您的文本框处理重做撤消。我错过了什么吗?

【讨论】:

  • 我的回答的缺点是,当您从文本框以外的任何地方设置属性时,重做/撤消堆栈会被删除。
【解决方案2】:

不是你想要的答案,但我会看看 Unity 之类的东西:

http://blogs.msdn.com/b/agile/archive/2011/03/21/interception-in-silverlight-demo.aspx

这是一篇 silverlight 帖子(但它也适用于 WPF 等)。基本上,您可以为您的模型创建一个拦截器并将代码注入属性 getter/setter。这样您就可以调用您的 N 级撤消/重做处理代码,而无需重新实现属性。

你所做的看起来更像是一个富模型而不是视图模型,因为标准视图模型会封装模型

我不想为模型上的每个属性编写一个额外的属性 getter/setter 代码

(他们展示了一个虚拟方法拦截器,但可以使用实例拦截器,我认为这会起作用,但听听他们关于拦截器的观点以及他们“监听”的范围)

编辑:

抱歉没有真正回答问题

以下是一些建议:

我不确定 EF 是否让您的实体类派生自基类,但这是需要考虑的一件事 - 您只需在基类中挂钩 PropertyChanged 事件并进行相应处理。您可以在基础上保留 Dictionary<string, object> 以跟踪“旧”值并处理撤消操作

我了解有关 EF 物化对象的一点点 - 你能不能在 UndoRedo 堆栈上添加一个标志,告诉它在物化对象时忽略任何属性更改?

必须有一些方法可以在部分实体类中覆盖,以告诉堆栈绕过任何撤消/重做信息

【讨论】:

  • 不确定 - 我自己没有使用过 MEF,但快速 Google 并没有找到任何有趣的结果
  • 我唯一能找到的是:msdn.microsoft.com/en-us/magazine/gg650670.aspx,它说 MEF 完全没有拦截(但它是从 2011 年 3 月开始的)
猜你喜欢
  • 2016-06-28
  • 2013-12-02
  • 2015-05-11
  • 2019-06-11
  • 1970-01-01
  • 1970-01-01
  • 2016-11-27
  • 2012-02-22
  • 1970-01-01
相关资源
最近更新 更多