【问题标题】:Change Notification in MVVM HierarchiesMVVM 层次结构中的更改通知
【发布时间】:2011-01-01 20:59:26
【问题描述】:

假设在一些抽象的 ViewModel 基类中,我有一个普通的旧属性,如下所示:

public Size Size
{
    get { return _size; }
    set
    {
        _size = value;
        OnPropertyChanged("Size");
    }
}

然后我创建一个更具体的 ViewModel,继承前一个,它包含以下属性:

public Rect Rectangle
{
    get { return new Rect(0, 0, _size.Width, _size.Height); }
}

现在,在某些 View 类中,我绑定到上述 ViewModel 的 Rectangle 属性。一切正常,直到我改变尺寸。当Size 更改时,Rectangle 不知道它并且更改不会传播到视图。由于Rectangle 在子类中,我不能简单地将OnPropertyChanged("Rectangle") 添加到Size 设置器中。

现在想象一下,我有许多不同的属性,例如 Rectangle,它们都依赖于基类属性,并且这些更改都没有被传播。我需要一些轻量级和优雅的链接更改通知的方式,最好是不需要大量代码且不会强迫我使用依赖属性的方式。

显然这里有很多丑陋的解决方案——我正在寻找的是干净和聪明的东西。在我看来,这将是一个非常常见的场景,而且在我看来,可能有一种 MVVM 友好 的方式来做到这一点。

【问题讨论】:

    标签: wpf mvvm inotifypropertychanged


    【解决方案1】:

    我使用 Josh Smith 的 PropertyObserver,您可以从他的 MVVM Foundation 库中获得它,地址为 http://mvvmfoundation.codeplex.com/

    用法:

    _viewmodel_observer = new PropertyObserver<OtherViewModel>(_OtherViewModel)
       .RegisterHandler(m => m.Size, m => RaisePropertyChanged(Rectangle);
    

    Brian 的属性方法也不错。我喜欢 PropertyObserver 的一件事是我可以执行任意代码;允许我检查可能使我避免加注或一起执行其他操作的条件。

    【讨论】:

      【解决方案2】:

      我最近写了一篇关于这个确切问题的博客。我在 Rectangle 中包含了一个 [DependsUpon("Size")] 属性。我真的很喜欢这种方法,因为它将依赖知识与创建依赖的代码一起保存,而不是相反。

      看一看:http://houseofbilz.com/archive/2009/11/14/adventures-in-mvvm----dependant-properties-with-inotifypropertychanged.aspx

      【讨论】:

      • 好主意。我对其进行了一些更改以在构建时创建字典(出于性能原因),但这个概念是合理的。
      • 链接已损坏。有没有人有一个实现这个的好方法的例子?
      • 很抱歉。我在这里有那个帖子的存档。它已经过时了 10 年,所以我完全不确定这些信息是否仍然相关,但这是存档文本:github.com/BrianGenisio/briangenisio.github.com/blob/…
      【解决方案3】:

      可能是因为我是一个 VB 人,但在您的 Rectangle 代码中,看起来您正在访问私有 _size 声明而不是 Public Size 属性,该属性不会触发 OnPropertyChanged 事件以提醒视图。

      我也可能不正确,但 Rectangle 不应该是一个实际的对象,而 Size 是该对象的属性吗?也许这就是你正在做的......一些 C# 方法对我来说仍然很陌生。

      【讨论】:

      • 我正在使用私有 _size 声明,但这无关紧要。那是一个吸气剂,而不是一个二传手。需要在 Size 更改时通知视图 Rectangle 正在更改,而不是在访问 Rectangle 时。
      【解决方案4】:

      干净的 MVVM 方法是使用 Messenger 订阅/通知机制(如 Josh Smith 的 MvvmFoundation

      在某处创建一个单例 Messenger 对象 - 主 App 类始终是这样做的好地方

      public partial class App : Application
      {
          private static Messenger _messenger;
          public static Messenger Messenger
          {
              get
              {
                  if (_messenger == null)
                  {
                      _messenger = new Messenger();
                  }
                  return _messenger;
              }
          }
      }
      

      在基类的 Size setter 中,通知更改:

      public Size Size
      {
          get { return _size; }
          set
          {
              _size = value;
              OnPropertyChanged("Size");
      
              App.Messenger.NotifyColleagues("SIZE_CHANGED");
          }
      }
      

      现在您可以让继承的 ViewModel 监听这些更改,并根据需要引发 PropertyChanged 事件...

      public MyViewModel : MyViewModelBase
      {
          public MyViewModel()
          {
              App.Messenger.Register("SIZE_CHANGED", () => OnPropertyChanged("Rectangle"));
          }
      }
      

      当然 - 您可以根据需要为此消息添加任意数量的订阅 - 每个需要更改以通知给视图的属性一个...

      希望这会有所帮助:)

      【讨论】:

      • 这与在父级中为子级覆盖 OnPropertyChanged 完全相同,不确定它是否真的使它更干净
      • 这不是一个坏主意,但我想要更轻量级的东西。
      • 使用 Messenger 方法的好处可能是在您具有深度嵌套的层次结构的情况下,将属性更改事件链接起来会很不方便并且可能效率低下。
      【解决方案5】:

      您可以像这样在派生的 ViewModel 中简单地覆盖 OnPropertyChanged:

      protected override void OnPropertyChanged(string propertyName) {
          base.OnPropertyChanged(propertyName);
          if (propertyName == "Size") {
              base.OnPropertyChanged("Rectangle");
          }
      }
      

      另一种可能... 不久前,我整理了一个非常不错的 ViewModel 基类,它支持以下属性的属性:

      [DependsOn("Size")]
      public Rect Rectangle {
          get { new Rect(0,0,Size.Width, Size.Height); }
      }
      

      然后 ViewModel 基类在运行时收集这些 DependsOnAttribute,在其 OnPropertyChanged 方法中,它基本上只是查看发生属性更改时需要使哪些其他属性无效。

      【讨论】:

      • 重写 OnPropertyChanged 绝对是一个解决方案,但不是一个优雅的解决方案。不过,我更喜欢你的第二个建议。
      • 并非所有解决方案都要求优雅。覆盖 OnPropertyChanged 非常简单,在许多情况下我仍然会这样做,除非依赖关系变得很复杂,这导致我采用第二种方法。
      • 你是对的,但请记住在我的问题中我确实说过,“现在想象一下,我有许多不同的属性,比如 Rectangle,它们都依赖于基类属性,而这些变化都不是被传播。”因此,假设层次结构已经变得多毛了。
      • 说实话,我对基于属性的方法也不完全满意。我一直在玩弄 DSL 来创建 ViewModel,它可以确定哪些属性应该触发相关属性更改,但我还没有达到令人满意的程度。
      猜你喜欢
      • 2018-06-30
      • 2012-09-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-07
      • 1970-01-01
      • 1970-01-01
      • 2020-12-23
      相关资源
      最近更新 更多