ViewModel和Model之间的WPF绑定

时间:2014-06-19 14:00:39

标签: c# wpf mvvm

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

当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,而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"); }
     }

}

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

4 个答案:

答案 0 :(得分:1)

为了避免代码重复,您可以创建一个派生自BaseViewModel的类来实现您的属性,并ViewModelAViewModelB扩展它。但是,这并没有解决保持每个实例更新的问题。

为此,您可以:

  • 使用其中一条评论中建议的静态类(您当前的解决方案)或Singleton。这很简单但有潜在的问题,如竞争条件和耦合。
  • 在每个ViewModel中重复您的ShowImages绑定属性,并通过订阅ShowImagesChanged事件进行更新。这可以通过从UI执行的命令发布。我说这是WPF方法,并且有利于将ShowImages州管理与其消费分离。
  • ShowImages更新责任分配给单个ViewModel,并在其他ViewModel中订阅其PropertyChanged,以便相应地更新。比第一种选择更好,但仍然是巨大的耦合。

答案 1 :(得分:1)

仅为该对象模型引发PropertyChange通知。

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

我建议您在StateModel使用单身人士,并让其他模型订阅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; }
    }
}

答案 2 :(得分:1)

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

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

我的方法是:

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

通过这种方式,您可以让ViewModels彼此分离。尽管如此,虽然ViewModel彼此不了解,但它提供了一种在它们之间交换数据的方法。

就个人而言,我已多次使用Caliburn Micro MVVM框架,但应该有足够的其他MVVM框架提供相同的功能以满足您的需求。

Calibiurn Micro文档以及调度程序的使用方式如下:Event Aggregator

答案 3 :(得分:0)

为什么要重复属性?只需绑定到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>

这样每个绑定都可以使用一个属性并对一个属性做出反应。如果您需要一些自定义代码,您可以根据需要订阅PropertyChanged StateController并采取措施。