2010-08-26 88 views
7

我想了解如何更新UI,如果我有一個只讀屬性是依賴於另一個屬性,以便更改一個屬性更新兩個UI元素(在這種情況下一個文本框和一個只讀的文本框,例如:WPF INotifyPropertyChanged爲鏈接的只讀屬性

public class raz : INotifyPropertyChanged 
{ 

    int _foo; 
    public int foo 
    { 
    get 
    { 
     return _foo; 
    } 
    set 
    { 
     _foo = value; 
     onPropertyChanged(this, "foo"); 
    } 
    } 

    public int bar 
    { 
    get 
    { 
     return foo*foo; 
    } 
    } 

    public raz() 
    { 

    } 

    public event PropertyChangedEventHandler PropertyChanged; 
    private void onPropertyChanged(object sender, string propertyName) 
    { 
    if(this.PropertyChanged != null) 
    { 
     PropertyChanged(sender, new PropertyChangedEventArgs(propertyName)); 
    } 
    } 
} 

我的理解是,欄將不會自動更新UI時富被修改請告訴我正確的方式做到這一點

回答

6

一個辦法?表明酒吧已經改變了,在foo setter中加入了一個叫onPropertyChanged(this, "bar")的電話。我知道,地獄醜陋,但是你知道。

如果foo是在祖先類中定義的,或者您無權訪問setter的實現,我想您可以訂閱PropertyChanged事件,以便當您看到「foo」更改時,您也可以發出「酒吧」變更通知。在自己的對象實例上訂閱事件同樣很難,但會完成工作。

+2

我不明白你爲什麼認爲這是特別醜陋的火災onPropertyChanged(這,「酒吧」)?我可以看到,如果.bar依賴於3個屬性,並且你在所有三個setter中都有這個調用,但是他可以將計算從.bar的property-getter移到一個調用update的計算方法中方法。 – Goblin 2010-08-26 19:16:59

+4

這是醜陋的,因爲您將屬性B的屬性B的適當行爲放置在屬性A的設置器中所需的邏輯。可以有任何數量的屬性取決於A的值。必須修改A以滿足其他屬性的需要使用A不明顯且不可擴展。 – dthorpe 2010-08-26 19:41:04

+1

更清潔的解決方案應該是有某種方式來指示B依賴於A,並且當A改變B時也應該指示值改變。上面的第二個選項,以聽取自己的PropertyChanged事件,是在這種風格,但聽你自己的事件也有點糾結。 – dthorpe 2010-08-26 19:45:17

0

根據計算的花費和您期望使用的頻率,將它設置爲私有屬性並在設置foo時計算該值可能是有益的,而不是在運行時計算bar get被調用。這基本上是一個緩存解決方案,然後您可以將屬性更改通知作爲bar私人設置器的一部分來執行。我通常更喜歡這種方法,主要是因爲我使用AOP(通過Postsharp)來實現實際的INotifyPropertyChanged樣板。

-Dan

5

你可以簡單地調用

OnPropertyChanged(this, "bar"); 
從任何地方

在這個類...你冷,甚至是這樣的:

public raz() 
    { 
     this.PropertyChanged += new PropertyChangedEventHandler(raz_PropertyChanged); 
    } 

    void raz_PropertyChanged(object sender, PropertyChangedEventArgs e) 
    { 
     if(e.PropertyName == "foo") 
     { 
      onPropertyChanged(this, "bar"); 
     } 
    } 
+0

這似乎是一個更好的可擴展解決方案,當我需要重複一堆屬性時。 – tbischel 2010-08-26 23:21:39

+1

訂閱PropertyChanged事件並觸發欄更改通知正是PRISM在其Commands Quickstart中執行的操作。他們的例子更好 - 如果(propertyName ==「Price」|| propertyName ==「Quantity」|| propertyName ==「Shipping」) { this.NotifyPropertyChanged(「Total」); } – RichardOD 2011-07-09 11:42:50

8

如果這是一個嚴重的問題(通過「嚴重」,我的意思是你有一個不平凡數量的依賴只讀屬性),你可以製作屬性依賴關係圖,例如:

private static Dictionary<string, string[]> _DependencyMap = 
    new Dictionary<string, string[]> 
{ 
    {"Foo", new[] { "Bar", "Baz" } }, 
}; 

,然後引用它在OnPropertyChanged:

PropertyChanged(this, new PropertyChangedEventArgs(propertyName)) 
if (_DependencyMap.ContainsKey(propertyName)) 
{ 
    foreach (string p in _DependencyMap[propertyName]) 
    { 
     PropertyChanged(this, new PropertyChangedEventArgs(p)) 
    } 
} 

這不是從本質上只是把多個在Foo二傳手OnPropertyChanged調用了很多不同的,因爲你必須更新每一個新的依賴的依賴地圖您添加的財產。

但它確實能夠實現PropertyChangeDependsOnAttribute並使用反射掃描類型並構建依賴關係圖。這樣你的財產會看起來像這樣:

[PropertyChangeDependsOn("Foo")] 
public int Bar { get { return Foo * Foo; } } 
+0

你可以顯示你的'PropertyChangeDependsOn'系統的實現嗎? – Shimmy 2012-01-13 10:31:02

+0

唉,我只聲稱可以實現它。實際上實施它不是我需要做的事情。 – 2012-01-13 18:04:36

+0

謝謝。我自己實現它,爲避免重新加載與反射的關係,我沒有多少麻煩製作高效緩存。 – Shimmy 2012-01-15 02:31:02

2

如果你只用於UI的目的,你可以完全從你的模型中刪除它。您可以將UI元素綁定到foo屬性,並使用自定義值轉換器將結果從foo更改爲foo * foo。

在WPF中通常有很多方法來完成同樣的事情。很多時候,沒有一個正確的方法,只是個人喜好。

+0

在我的情況下,用戶需要看到它,程序也需要訪問它,但這聽起來很有趣。 – tbischel 2010-08-26 23:20:17

0

我非常確定它必須以聲明的方式成爲可能,但我第一次嘗試解決這個問題是失敗的。 我想要完成的解決方案是使用Lambda表達式來定義這一點。 由於可以解析表達式(??),應該可以解析表達式並附加到所有NotifyPropertyChanged事件以獲取有關依賴數據更改的通知。

在ContinousLinq中,這對收藏非常適用。

private SelfUpdatingExpression<int> m_fooExp = new SelfUpdatingExpression<int>(this,()=> Foo * Foo); 

public int Foo 
{ 
    get 
    { 
     return m_fooExp.Value; 
    } 
} 

但unfortionatly我缺乏對fundamentional知道如何表達和LINQ :(

+0

WPF可以直接綁定自我更新表達式嗎? - 如果是這樣,爲什麼不只是返回該類型而不是計算的int? – BrainSlugs83 2014-09-05 21:43:52

9

我意識到這是一個老問題,但它的「鏈接屬性NotifyPropertyChanged」的第一個谷歌的結果,所以我覺得它是適當的,這樣有一些具體的代碼添加這個答案

我用羅伯特Rossney的建議,並創建一個自定義屬性,然後在基本視圖模型的PropertyChanged事件用它

屬性類。:

在我基本視圖模型(所有其他WPF視圖模型繼承):

public abstract class BaseViewModel : INotifyPropertyChanged 
{ 
    protected Dictionary<string, List<string>> DependencyMap; 

    protected BaseViewModel() 
    { 
     DependencyMap = new Dictionary<string, List<string>>(); 

     foreach (var property in GetType().GetProperties()) 
     { 
      var attributes = property.GetCustomAttributes<DependsOnPropertyAttribute>(); 
      foreach (var dependsAttr in attributes) 
      { 
       if (dependsAttr == null) 
        continue; 

       var dependence = dependsAttr.Dependence; 
       if (!DependencyMap.ContainsKey(dependence)) 
        DependencyMap.Add(dependence, new List<string>()); 
       DependencyMap[dependence].Add(property.Name); 
      } 
     } 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) 
    { 
     var handler = PropertyChanged; 
     if (handler == null) 
      return; 

     handler(this, new PropertyChangedEventArgs(propertyName)); 

     if (!DependencyMap.ContainsKey(propertyName)) 
      return; 

     foreach (var dependentProperty in DependencyMap[propertyName]) 
     { 
      handler(this, new PropertyChangedEventArgs(dependentProperty)); 
     } 
    } 
} 

現在,這可以讓我輕鬆地標記屬性,就像這樣:

public int NormalProperty 
{ 
    get {return _model.modelProperty; } 
    set 
    { 
     _model.modelProperty = value; 
     OnPropertyChanged(); 
    } 
} 

[DependsOnProperty(nameof(NormalProperty))] 
public int CalculatedProperty 
{ 
    get { return _model.modelProperty + 1; } 
} 
+0

如果我在A類中擁有AnyState屬性,那麼該怎麼辦?基本上來自兩個其他屬性,每個屬性都在一個單獨的類中。 prop A get {return B.IsRunning || C.IsRunning;} B類也有一個IsRunning屬性和C。 – Luishg 2018-02-08 16:26:41

+0

@Luishg如果屬性位於不同的類中,則需要使用其他解決方案。該屬性僅適用於單個類的計算屬性。 – 2018-02-08 16:36:10