【问题标题】:WPF binding between ViewModel and ModelViewModel 和 Model 之间的 WPF 绑定
【发布时间】:2014-06-19 14:00:39
【问题描述】:

在对这个问题进行了重大修改后,我希望现在已经清楚了。

当 1 次更改会影响多个属性时,我对 WPF 中的绑定感到非常困惑。

我经常使用 VVM 将我的 ViewModel 绑定到我的 View,我会说我可以接受。

我正在尝试实现一个状态控制器。这意味着,无论我在 UI 的一部分中进行什么设置,反射都会彻底结束。

例如,在我的 UI 部分,我可以打开或关闭某个功能,例如“显示图像”

当我进行此更改时,我希望我的应用程序中的所有内容都能得到通知并采取相应措施。

所以,我的 StateController 类将有一个属性

public bool ShowImages

在我看来,我可能会有类似的东西

<image Visible ="{Binding ShowImages", Converter={StaticConverter ConvertMe}}" />

我遇到的问题是如何让 StateController 向我的所有 ViewModel 发出警报。

目前,我假设在每个 ViewModel 中我必须重复相同的属性

public bool ShowImages

EG

public class StateController : BaseViewModel
{
    public bool ShowImages{get;set;}//imagine the implementation is here
}

public class ViewModelB : BaseViewModel
{
    public bool ShowImages{}//imagine the implementation is here
}

public class ViewModelB : BaseViewModel
{
    public bool ShowImages{}//imagine the implementation is here
}

所以,我的问题是,如果我更新了 ViewModelB.ShowImages,我将如何首先通知 StateController,而后者又会更新所有 ViewModel。

这是 INotifyPropertyChanged 可以自动为我做的事情,因为它们都共享相同的 propertyName,还是我必须手动实现逻辑,例如

public static class StateController
{
    public bool ShowImages{get;set;}//imagine the implementation is here
}

public class ViewModelA : BaseViewModel
{
    public bool ShowImages
    {
        get { return StateController.ShowImages; }
        set { StateControllerShowImages = value;
              OnPropertyChanged("ShowImages"); }
     }
}

public class ViewModelB : BaseViewModel
{
    public bool ShowImages
    {
        get { return StateController.ShowImages; }
        set { StateControllerShowImages = value;
              OnPropertyChanged("ShowImages"); }
     }

}

我讨厌上述实现的想法,但它确实显示了我想要实现的目标。我只是希望有更好的方法!

【问题讨论】:

  • 你有两个不同的对象,改变其中一个的道具。 WPF 如何知道这些对象具有相同的业务角色?
  • 我记得最近写了一个answer,关于使用单例在两个类之间共享一个属性。这就是你想要做的吗?
  • 您应该通知您的视图有关更改。如果您有两个独立的 props 引用相同的数据模型 obj,则应更改模型,然后为这两个 props 调用 RaisePropertyChanged 以使 WPF 重新读取它们。
  • @Rachel,我不这么认为。可能是如果我说我正在尝试实现 StateController 更容易理解。因为有很多地方可以更改配置,这些应该提醒 StateController,然后通知所有 ViewModel 并相应更新......
  • 我不得不承认我无法理解这个问题是关于什么的,但我所能做的就是建议您阅读 MSDN 上的Data Binding Overview 页面。此页面是一个很好的资源,广泛涵盖了非常广泛的数据绑定问题。希望阅读后,您将能够回答自己的问题。

标签: c# wpf mvvm


【解决方案1】:

PropertyChange 通知仅针对该一个对象模型引发。

因此,提出ClassA"Name" 属性的更改通知只会在绑定到特定ClassA.Name 的情况下更新UI。它不会触发任何ClassB.NameClassA.Name 的其他实例的更改通知。

我建议在这里为您的 StateModel 使用 Singleton,并让您的其他模型订阅 StateModel.PropertyChanged 事件以了解它是否应该更新,例如 this answer

public ViewModelA
{
    public ViewModelA()
    {
        StateController.Instance.PropertyChanged += StateController_PropertyChanged;
    }

    void StateController_PropertyChanged(object sender, NotifyPropertyChangedEventArgs e)
    {
        // if singleton's ShowImages property changed, raise change 
        // notification for this class's ShowImages property too
        if (e.PropertyName == "ShowImages")
            OnPropertyChanged("ShowImages");
    }

    public bool ShowImages
    {
        get { return StateController.Instance.ShowImages; }
        set { StateController.Instance.ShowImages = value; }
    }
}

【讨论】:

  • So raising a change notification of the "Name" property of ClassA will only update the UI in cases where it's bound to that specific ClassA.Name 我被告知不同 - 这是完美的答案!
  • 我只想指出这个例子中的内存泄漏。 ViewModelA 需要是一次性的,并且与 dispose 上的 PropertyChanged 事件解除绑定。
【解决方案2】:

如果我对您的理解正确,您正在寻找一种允许不同 ViewModel 相互通信的机制。

一种可能的方法是实现观察者模式(可以在此处找到代码示例:"Observer pattern with C# 4")。通过这种方式,您的 ViewModel 相互订阅以接收来自“发布者”的更改通知,即其值发生更改的 ViewModel。您可以很好地控制谁接收来自哪个发布者的通知。这种方法的缺点是模型之间的紧密耦合。

我的方法是这样的:

使用消息调度程序。您的 ViewModel 可以订阅某种类型的消息,例如ShowImagesChanged。如果您的任何 ViewModel 更改了 ShowImages 属性,则该 ViewModel 会调用调度程序以发送带有您当前值的 ShowImagesChanged 消息。

这样,您可以让 ViewModel 彼此分离。尽管如此,尽管 ViewModel 彼此不认识,但这提供了一种在它们之间交换数据的方法。

就我个人而言,我已经多次使用 Caliburn Micro MVVM 框架,但应该有足够多的其他 MVVM 框架提供相同的功能来满足你的口味。

Calibiurn Micro 文档以及调度程序的使用方便程度在此处:Event Aggregator

【讨论】:

  • 我不断地看到观察者模式和事件聚合器,但在我了解基础知识之前还没有找到学习其他东西的动力。 +1,谢谢
  • @MyDaftQuestions,我认为设计模式是基础。对于需要在编程中一遍又一遍地解决的问题,它们是经过时间考验的解决方案。由于设计模式已被证明是行之有效的解决方案,我强烈建议学习它们的基本原理并向它们学习。我希望有人在我刚开始的时候告诉他们,这会节省我的很多神经。 :-)
【解决方案3】:

为避免代码重复,您可以创建一个派生自 BaseViewModel 的类,该类实现您的属性并让 ViewModelAViewModelB 对其进行扩展。但是,这并不能解决保持每个实例更新的问题。

为此,您可以:

  • 按照其中一个 cmets 中的建议,使用静态类(您当前的解决方案)或 Singleton。这很简单,但存在竞争条件和耦合等潜在问题。
  • 在每个 ViewModel 中重复您的 ShowImages 绑定属性,并通过订阅 ShowImagesChanged 事件来更新它。这可以通过从 UI 执行的命令发布。我想说这是 WPF 方法,它的好处是可以将 ShowImages 状态管理与其消费解耦。
  • ShowImagesupdate 职责分配给单个ViewModel,并在其他ViewModel 中订阅其PropertyChanged,以便它们进行相应更新。比第一个选项更好,但仍然存在巨大的耦合。

【讨论】:

  • 啊,第二个要点很有趣......但我不确定这些属性也会订阅什么。 INotifyProeprtyChanged 没有做的新事件会做什么?
  • @MyDaftQuestions 我添加了第三个选项,我认为这就是您的建议。在该选项中,您仍然可以提前使用命令,但缺点是要使用INotifyPropertyChanged,您需要引用您订阅的 ViewModel。这就是您通过使用事件聚合器获得的收益。
  • 我对所有这些事件订阅都有不好的预感。如果您不小心,这很容易导致严重且难以发现的内存泄漏。
  • 我不会使用订阅,除非两个完全不相关的对象需要相互通信。在这种处理配置属性的场景中,我认为共享StateController 可能是更好的选择,因为我回答了here
  • @JensH 我确实与您分享了您对 .NET 事件和引用泄漏的不好感觉,但我指的是事件聚合器模式。
【解决方案4】:

为什么要重复属性?只需绑定到StateController 本身。 假设我们有单身StateController

public class StateController : INotifyPropertyChanged
{
  private static StateController instance;
  public static StateController Instance {
     get { return instance ?? (instance = new StateController()); }
  }

  //here`s our flag
  private bool isSomething;
  public bool IsSomething
  {
     get { return isSomething; }
     set
     {
        isSomething = value;
        PropertyChanged(this, new PropertyChangedEventArgs("IsSomething"));
     }
  }

  private StateController(){}

  public event PropertyChangedEventHandler PropertyChanged = delegate { };
}

然后在基础 VM 类中只引用这个控制器:

public StateController Controller { get { return StateController.Instance; } }

在需要的地方像这样绑定:

<CheckBox IsChecked="{Binding Controller.IsSomething}">
    Test
</CheckBox>

这样,每个绑定都将使用一个属性并对一个属性做出反应。如果您需要一些自定义代码来工作,您可以在需要的地方订阅 PropertyChangedStateController 并采取行动。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-06-14
    • 2011-01-21
    • 2011-06-25
    • 2014-06-16
    • 1970-01-01
    • 2019-06-01
    • 2013-11-04
    相关资源
    最近更新 更多