2012-02-20 305 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; 
    } 
}