视图模型之间的WPF数据绑定

本文关键字:WPF 数据绑定 之间 模型 视图 | 更新日期: 2023-09-27 18:18:17

我有一个问题,我有困难正确措辞,因此还没有能够找到一个满意的答案。也许这里有人能给我指个方向。

我在WPF中使用MVVM来创建一个类似uml的建模工具。出于所有的意图和目的,让我们坚持使用UML类比。

我基本上有4个viewmodel相关在这里:CanvasViewModel, ClassViewModel, MemberViewModel, TypeViewModel。它们都实现了相同的接口,暴露了bool Valid属性。

可以想象,有一个全局画布,画布上有n个类,类中有n个成员,每个成员有1个类型。它可以这样表示:

Canvas
{
  Person (Class)
  {
    Age (Member)
    {
      int (Type)
    }
    Name (Member)
    {
      string (Type)
    }
  }
  House (Class)
  {
    Price (Member)
    {
      currency (Type)
    }
  }
}

它实现了如下的图片:(http://www.tutorialspoint.com/uml/images/uml_class_diagram.jpg)

我绑定了CanvasViewModel。如果它为假,则画布将显示一个大的红色搜索灯。我绑定了ClassViewModel。如果它为false,则类框可以做一个摆动动画。我绑定了MemberViewModel。如果它为false, listview就会闪烁红色。我绑定了TypeViewModel。 当然,Valid属性的实现非常简单(代码未测试):
// CanvasViewModel
public bool Valid { get { return Classes.All(x => x.Valid); } }
// ClassViewModel
public bool Valid { get { return Members.All(x => x.Valid); } }
// MemberViewModel
public bool Valid { get { return Type.Valid; } }

所以这个用例是:用户不小心将TypeViewModel设置为" int ",这是一个无效的类型。我希望所有4个ViewModels然后有他们的Valid属性评估为假。我希望这个事件通过所有正确的ViewModels(从Type -> Member -> Class -> Canvas)传播。

我究竟如何做到这一点,而不必在我的代码中到处摆弄像

这样可怕的行
var memberViewModel = new MemberViewModel(member);
memberViewModel.PropertyChanged += (o, e) => { if ( e.PropertyName == "Valid") OnPropertyChanged("Valid"); }
Members.Add(memberViewModel);

我不想把我的功能像这样串在一起。我更喜欢干净的绑定,具有明确的进入/退出、获取/设置或添加/删除函数。

是的,不是ViewModel和GUI之间的绑定,而是ViewModels之间的绑定。我试着玩OnPropertyChanged和CoerceValue。但我并不完全清楚它们的目的。它看起来好像在我的情况下,成员OnPropertyChanged实现看起来像这样

public static void OnPropertyChanged(...)
{
  MyParentClass.CoerceValue(ClassViewModel.ValidProperty);
}

我想这不会太糟糕,除了我不知道什么责任CoerceValue实际上有。评估在那里进行吗?是否该属性假定的返回值的CoerceValue?是否像OnPropertyChanged代表"set"和CoerceValue代表"get"一样简单,就像在常规属性中一样?不管怎样,我没有让它工作,所以我怀疑我是否正确理解了它的目的。所有使用OnPropertyChanged/CoerceValue的例子基本上都是在同一个类中处理DependencyProperties(永远不会跨类)。

有什么办法可以解决这个相当普遍的问题吗?

欢呼,

Rene

视图模型之间的WPF数据绑定

处理这种情况的设计模式在这里进行了很好的讨论。在您的情况下,我喜欢选项1,在每个子对象中保存对父对象的引用。然后,您将在属性设置器中调用从子属性更改的父属性。这可能是最简单的解决方案,但它确实引入了亲子耦合,但似乎你不会有与他们描述的相同的担忧。我建议将ivalidation传递到子类的构造函数中,并在setter中检查它是否为空。如果使用mvvm框架

,还可以查看消息总线模式

使用反射和接口(或某种基类)。使用基类确定要为其设置值的对象类型,并向下迭代到每个类。大意是:

   interface IValid
    {
        bool Valid { get; set; }
    }
    class Canvas : IValid, INotifyPropertyChanged
    {
        private bool valid;
        public bool Valid
        {
            get { return this.valid; }
            set
            {
                this.valid = value;
                this.OnPropertyChanged("Valid");
                this.SetPropertyValues(this);
            }
        }

        public Canvas()
        {
            this.Person = new Person();
        }
        public Person Person { get; set; }

        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string propertyName)
        {
            var handler = this.PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        private void SetPropertyValues(IValid obj)
        {
            var properties = obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
            foreach (var property in properties)
            {
                if (property.CanRead)
                {
                    var validObject = property.GetValue(obj) as IValid;
                    if (validObject != null)
                    {
                        validObject.Valid = obj.Valid;
                        SetPropertyValues(validObject);
                    }
                }
            }
        }

    }
    class Person : IValid, INotifyPropertyChanged
    {
        private bool valid;
        public bool Valid
        {
            get { return this.valid; }
            set
            {
                this.valid = value;
                this.OnPropertyChanged("Valid");
            }
        }

        public Person()
        {
            this.Age = new Age();
        }
        public Age Age { get; set; }

        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string propertyName)
        {
            var handler = this.PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
    class Age : IValid, INotifyPropertyChanged
    {
        private bool valid;
        public bool Valid
        {
            get { return this.valid; }
            set
            {
                this.valid = value;
                this.OnPropertyChanged("Valid");
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string propertyName)
        {
            var handler = this.PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }