2012-02-20 205 views
1

C类实现INotifyPropertyChanged。依赖于其他属性的依赖属性

假设C具有长度,宽度和面积属性,其中Area = Length * Width。 中的更改可能会导致面积发生更改。所有三个都是绑定的,即UI期望所有三个都通知其值的变化。

当“长度”或“宽度”更改时,它们的setter调用NotifyPropertyChanged。

我应该如何处理计算的面积属性?目前我能想到的模式是在NotifyPropertyChanged中检测更改的属性是长度还是宽度,如果是这种情况,则为Area启动一个附加的PropertyChanged通知。但是,这需要我在NotifyPropertyChanged内部维护依赖关系图,我认为这是一种反模式。

所以,我的问题是:我应该如何编码依赖于其他依赖属性的依赖属性?

编辑:这里的人认为,长和宽也呼吁NotifyPropertyChanged的区域。再次,我认为这是一种反模式。一个属性(恕我直言)不应该知道谁取决于它,因为不应该NotifyPropertyChanged。只有财产应该知道它依赖于谁。

+2

不要混淆[依赖性](HTTP :// MSDN。microsoft.com/en-us/library/ms752914.aspx)与实现INotifyPropertyChanged的类的属性。这不是一回事。 – Clemens 2012-02-20 13:16:05

+1

如果你真的不喜欢它。将您的视图模型注册到它自己的PropertyChanged事件中,侦听宽度和长度的属性更改,然后再次提高Area的更改。但是,它再一次证明了它可以提高多个属性。实际上,募集将永远不会调用财产的创造者,只有吸收者才是安全的。 – dowhilefor 2012-02-20 13:59:40

+0

Duplicate http://stackoverflow.com/questions/5440121/databinding-to-calculated-field – 2012-02-26 13:28:08

回答

0

LengthWidth性质改变你火PropertyChangedArea除了烧制它要么LengthWidth

这是基于备份域和方法OnPropertyChanged火的PropertyChanged事件非常简单的实现:

public Double Length { 
    get { return this.length; } 
    set { 
    this.length = value; 
    OnPropertyChanged("Length"); 
    OnPropertyChanged("Area"); 
    } 
} 

public Double Width { 
    get { return this.width; } 
    set { 
    this.width = value; 
    OnPropertyChanged("Width"); 
    OnPropertyChanged("Area"); 
    } 
} 

public Double Area { 
    get { return this.length*this.width; } 
} 

做它像这样肯定是不反模式。这正是这样做的模式。作为该课程的实施者,您知道当Length发生变化时,Area也会发生变化,您可以通过提高适当的事件来对其进行编码。

+0

但如果“宽度”和“长度”是在另一个类中,那么你如何为'Area'增加'PropertyChanged'?检查这个问题的答案:http://stackoverflow.com/questions/43653750/raising-propertychanged-for-a-dependent-property-when-a-prerequisite-property-in-another-class – Jogge 2017-04-28 05:26:01

+0

虽然这是我同样如此,当你有一个中等大小的课堂时,这会很快失去控制。你开始狩猎你把你的OnPropertyChanged()调用改变的属性。当您编辑属性A时,您不应该在包含数百个,有时包含数千行代码的类文件中寻找OnPropertyChanged(“A”)调用。 – 2017-12-08 21:06:07

0

然后,您应该在长度和宽度属性设置器中提升两次。一个用于实际属性,另一个用于Area属性。

例如:这里

private int _width; 
public int Width 
{ 
    get { return _width; } 
    set 
    { 
     if (_width == value) return; 
     _width = value; 
     NotifyPropertyChanged("Width"); 
     NotifyPropertyChanged("Area"); 
    } 
} 

市民提出,长度和宽度也呼吁 NotifyPropertyChanged的区域。再次,我认为这是一个反模式。一个属性(恕我直言)不应该知道谁取决于 它,因为不应该NotifyPropertyChanged。只有财产应该是 意识到它依赖于谁。

这不是反模式。实际上,你的数据封装在这个类中,所以这个类知道什么时候和什么改变了。除此之外,你不应该知道面积取决于宽度和长度。所以告知听众Area最合乎逻辑的地方是宽度和长度设置器。

的属性(恕我直言)不应该知道的谁依赖于它,因为 不应NotifyPropertyChanged。

它不会破坏封装,因为你是在同一个班级,在相同的数据结构。

额外的信息是knockout.js(一个javascript mvvm库)有一个概念,它可以访问这个问题:Computed Observables。所以我相信这是绝对可以接受的。

+0

在Ember.js框架中,计算属性表示这个相同的功能,但它只会使计算属性声明依赖的属性名称,而不是像解决方案中所暗示的那样。 – Epirocks 2017-10-17 00:13:56

+0

虽然它看起来很相似,但是emberjs计算属性是另一回事。通过INotifyPropertyChanged和你在这里看到的你甚至不需要声明依赖关系。 'NotifyPropertyChanged'不是像'function(){}。property('foo','bar')'这样的依赖声明,它只是触发一个事件。 – 2017-10-17 15:39:56

+0

这就是问题所在,不是你要在一个依赖的属性中提升事件,而是隐式地声明它。看起来不正确。如果你所举的事件也依赖于对方,那该怎么办?如果B依赖于A而C依赖于B和D.对于所有人来说,属性都会调用Notify,尽管D只是模糊地关心A并且可能在语义上不相关。例如,为什么Age字段应该关注在12月4日创建的与客户有关的订单... – Epirocks 2017-10-18 16:32:06

2

这个问题一直困扰着我,所以我重新打开它。

首先,我想为任何考虑我个人“反模式”评论的人道歉。这里提供的解决方案实际上是如何在WPF中完成的。但是,恕我直言,它们是造成的不良做法,在其框架中存在缺陷。

我的要求是,information hiding导向决定了当B上的一个depeneds,A不应B的了解对于〔实施例,当B A派生,A不应该有代码说:“如果我的运行时类型真的是B,然后做这个和那个“。 Simiarily,当B 使用 A,A应该没有代码说:“如果调用此方法的对象是B,那么......”

所以这就意味着若物业B依赖于物业A,A不该成为负责直接提醒B的人。

相反,维护(正如我目前所做的)NotifyPropertyChanged中的依赖关系图也是一种反模式。该方法应该是轻量级的,并执行它所命名的状态,而不是维护属性之间的依赖关系。

所以,我认为所需的解决方案是通过aspect oriented programming:性能B应该使用“我依赖于(属性A)”属性,并且一些代码重写器应该创建依赖关系图并透明地修改NotifyPropertyChanged。今天,我是单一产品的程序员,所以我不能证明这个问题,但我认为这是正确的解决方案。

+1

不,你错了。信息隐藏在相同的数据结构(同一个类,没有继承)内是没有意义的。此外,INotifyPropertyChanged是一个具有一个事件的接口:PropertyChanged。谁实现这个接口,谁知道什么时候提出PropertyChanged。不需要AOP,这是一个完全不同的话题。 – 2012-02-27 15:19:33

3

下面是说明如何创建一个自动调用的PropertyChanged对视他人财产属性的自定义属性的文章:http://www.redmountainsw.com/wordpress/2012/01/17/a-nicer-way-to-handle-dependent-values-on-propertychanged/

的代码看起来就像这样:

[DependsOn("A")] 
[DependsOn("B")] 
public int Total 
{ 
    get { return A + B; } 
} 

public int A 
{ 
    get { return m_A; } 
    set { m_A = value; RaisePropertyChanged("A"); } 
} 

public int B 
{ 
    get { return m_B: } 
    set { m_B = value; RaisePropertyChanged("B"); } 
} 

我没试过它自己,但我喜欢这个想法

0

这是一个属性的可能实现:

public class DependentPropertiesAttribute : Attribute 
{ 
    private readonly string[] properties; 

    public DependentPropertiesAttribute(params string[] dp) 
    { 
     properties = dp; 
    } 

    public string[] Properties 
    { 
     get 
     { 
      return properties; 
     } 
    } 
} 

然后在基本视图模型,我们处理调用属性依赖的机制:

public class ViewModelBase : INotifyPropertyChanged 
{ 
    public ViewModelBase() 
    { 
     DetectPropertiesDependencies(); 
    } 

    private readonly Dictionary<string, List<string>> _dependencies = new Dictionary<string, List<string>>(); 

    private void DetectPropertiesDependencies() 
    { 
     var propertyInfoWithDependencies = GetType().GetProperties().Where(
     prop => Attribute.IsDefined(prop, typeof(DependentPropertiesAttribute))).ToArray(); 

     foreach (PropertyInfo propertyInfo in propertyInfoWithDependencies) 
     { 
      var ca = propertyInfo.GetCustomAttributes(false).OfType<DependentPropertiesAttribute>().Single(); 
      if (ca.Properties != null) 
      { 
       foreach (string prop in ca.Properties) 
       { 
        if (!_dependencies.ContainsKey(prop)) 
        { 
         _dependencies.Add(prop, new List<string>()); 
        } 

        _dependencies[prop].Add(propertyInfo.Name); 
       } 
      } 
     } 
    } 

    protected void OnPropertyChanged(params Expression<Func<object>>[] expressions) 
    { 
     expressions.Select(expr => ReflectionHelper.GetPropertyName(expr)).ToList().ForEach(p => { 
      RaisePropertyChanged(p); 
      RaiseDependentProperties(p, new List<string>() { p }); 
     }); 

    } 

    public event PropertyChangedEventHandler PropertyChanged = delegate { }; 

    protected virtual void RaisePropertyChanged(string propertyName) 
    { 
     PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
    } 

    protected void RaiseDependentProperties(string propertyName, List<string> calledProperties = null) 
    { 
     if (!_dependencies.Any() || !_dependencies.ContainsKey(propertyName)) 
      return; 

     if (calledProperties == null) 
      calledProperties = new List<string>(); 

     List<string> dependentProperties = _dependencies[propertyName]; 

     foreach (var dependentProperty in dependentProperties) 
     { 
      if (!calledProperties.Contains(dependentProperty)) 
      { 
       RaisePropertyChanged(dependentProperty); 
       RaiseDependentProperties(dependentProperty, calledProperties); 
      } 
     } 
    } 
} 

最后,我们在我们的视图模型定义依赖

[DependentProperties("Prop1", "Prop2")] 
public bool SomeCalculatedProperty 
{ 
    get 
    { 
     return Prop1 + Prop2; 
    } 
}