2016-08-11 42 views
6

在沒有set但是get取決於其他字段的情況下,c#中通知屬性在項目字段中更改的最佳方式是什麼?當字段依賴於另一個字段時,通知屬性更改的最佳方式

例如:

public class Example : INotifyPropertyChanged 
{ 
    private MyClass _item; 
    public event PropertyChangedEventHandler PropertyChanged; 

    public MyClass Item 
    { 
     get 
     { 
      return _item; 
     } 
     protected set 
     { 
      _item = value; 
      OnPropertyChanged("Item"); 
     } 
    } 

    public object Field 
    { 
     get 
     { 
      return _item.Field; 
     } 
    } 
#if !C#6 
    protected void OnPropertyChanged(string propertyName) 
    { 
     PropertyChangedEventHandler handler = PropertyChanged; 

     if (handler != null) 
     { 
      handler(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 
#else 
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null) 
    { 
     PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 
     // => can be called in a set like this: 
     // public MyClass Item { set { _item = value; OnPropertyChanged();} } 
     // OnPropertyChanged will be raised for "Item" 
    } 
#endif 
} 

什麼設置Item何時起一個對的PropertyChanged的"Field"最好的方式?我想在設置Item時撥打OnPropertyChanged("Field");,但如果我有很多字段,代碼將很快變得難看且不可維護。

編輯:

我不知道是否有一個功能/方法/屬性的工作是這樣的:

[DependOn(Item)] 
public object Field 
{ 
    get 
    { 
     return _item.Field; 
    } 
} 

=>當Item變化,所有的視場將通知更改的屬性。

它存在嗎?

+1

你可以實現的inotify在''MyClass' MyClass' –

+1

'Field'不會改變,實例_item被另一個替代。在我的例子中,'MyClass'包含一個xml文本。我做了一個webRequest獲得'MyClass'的新實例,並將它保存在'_item'中。 '_item.Field'不能通知已更改的屬性,因爲'Field'只是解析xml文件的get。 –

回答

6

一種方法是隻是調用OnPropertyChanged多次:

public MyClass Item 
{ 
    get 
    { 
     return _item; 
    } 
    protected set 
    { 
     _item = value; 
     OnPropertyChanged("Item"); 
     OnPropertyChanged("Field"); 
    } 
} 

這是不是很維護,但是。另一種選擇是一個setter從其他屬性添加到您只獲得屬性和設置:

public MyClass Item 
{ 
    get 
    { 
     return _item; 
    } 
    protected set 
    { 
     _item = value; 
     OnPropertyChanged("Item"); 
     Field = _item.Field; 
    } 
} 

public object Field 
{ 
    get 
    { 
     return _field; 
    } 
    private set 
    { 
     _field = value; 
     OnPropertyChanged("Field"); 
    } 
} 

存在一種使用屬性來指示性之間的這種關係沒有內置的機制,但是這將是可能的創建一個可以爲你做的幫手類。

我做了什麼,可能看起來像這裏很基本的例子:

[AttributeUsage(AttributeTargets.Property)] 
public class DepondsOnAttribute : Attribute 
{ 
    public DepondsOnAttribute(string name) 
    { 
     Name = name; 
    } 

    public string Name { get; } 
} 

public class PropertyChangedNotifier<T> : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 

    public PropertyChangedNotifier(T owner) 
    { 
     mOwner = owner; 
    } 

    public void OnPropertyChanged(string propertyName) 
    { 
     var handler = PropertyChanged; 
     if(handler != null) handler(mOwner, new PropertyChangedEventArgs(propertyName)); 

     List<string> dependents; 
     if(smPropertyDependencies.TryGetValue(propertyName, out dependents)) 
     { 
      foreach(var dependent in dependents) OnPropertyChanged(dependent); 
     } 
    } 

    static PropertyChangedNotifier() 
    { 
     foreach(var property in typeof(T).GetProperties()) 
     { 
      var dependsOn = property.GetCustomAttributes(true) 
            .OfType<DepondsOnAttribute>() 
            .Select(attribute => attribute.Name); 

      foreach(var dependency in dependsOn) 
      { 
       List<string> list; 
       if(!smPropertyDependencies.TryGetValue(dependency, out list)) 
       { 
        list = new List<string>(); 
        smPropertyDependencies.Add(dependency, list); 
       } 

       if (property.Name == dependency) 
        throw new ApplicationException(String.Format("Property {0} of {1} cannot depends of itself", dependency, typeof(T).ToString())); 

       list.Add(property.Name); 
      } 
     } 
    } 

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

    private readonly T mOwner; 
} 

這不是非常強勁(例如,你可以創建屬性之間,改變將獲得屬性的循環依賴關係陷入無限遞歸情況)。使用.NET 4.5和C#6功能也可以使它變得更簡單,但是我將把所有這些都作爲讀者的練習。它可能也不能很好地處理繼承。

要使用這個類:

public class Example : INotifyPropertyChanged 
{ 
    private MyClass _item; 
    private PropertyChangedNotifier<Example> _notifier; 

    public Example() 
    { 
     _notifier = new PropertyChangedNotifier<Example>(this); 
    } 

    public event PropertyChangedEventHandler PropertyChanged 
    { 
     add { _notifier.PropertyChanged += value; } 
     remove { _notifier.PropertyChanged -= value; } 
    } 

    public MyClass Item 
    { 
     get 
     { 
      return _item; 
     } 
     protected set 
     { 
      _item = value; 
      OnPropertyChanged("Item"); 
     } 
    } 

    [DependsOn("Item")] 
    public object Field 
    { 
     get 
     { 
      return _item.Field; 
     } 
    } 
    protected void OnPropertyChanged(string propertyName) 
    { 
     _notifier.OnPropertyChanged(propertyName); 
    } 
} 
+1

這就是我的實際做法。問題是我的課程尚未修復,我必須創建字段並刪除字段以供測試。這可能會導致誤導。如果字段「項目」忽略「依賴於它的字段,我只能關注新聞領域。更易維護,可避免錯誤 –

+1

我開發了一個類似的屬性(沒有看到您的編輯^^),但您的'PropertyChangedNotifier'看起來比我的代碼更好。我會用你的例子,謝謝。我只是添加測試,如果'dependent == propertyName' –

+0

爲了完整,在您的PropertyChangedNotifier中,展開屬性依賴於多個其他屬性(堆棧[DependsOn(「Item」)])的示例用法。 – tomosius

2

據我所知,有沒有內置的方法爲。我通常這樣做:

public class Foo : INotifyPropertyChanged 
{ 
    private Bar _bar1; 

    public Bar Item 
    { 
     get { return _bar1; } 
     set 
     { 
      SetField(ref _bar1, value); 
      ItemChanged(); 
     } 
    } 

    public string MyString 
    { 
     get { return _bar1.Item; } 
    } 

    private void ItemChanged() 
    { 
     OnPropertyChanged("MyString"); 
    } 
} 

public class Bar 
{ 
    public string Item { get; set; } 
} 

你沒有通過這種方式屬性的通知邏輯。這種方式在我看來更具可維護性,並且清楚這種方法的作用。另外,我更喜歡使用這種方法,我在SO上找到了某處,而不是類中的硬編碼名稱(如果屬性的名稱發生更改,它會中斷)。

OnPropertyChanged("MyString");成爲OnPropertyChanged(GetPropertyName(() => MyString));

其中GetPropertyName是:

public static string GetPropertyName<T>(Expression<Func<T>> propertyLambda) 
{ 
    if (propertyLambda == null) throw new ArgumentNullException("propertyLambda"); 

    var me = propertyLambda.Body as MemberExpression; 

    if (me == null) 
    { 
     throw new ArgumentException("You must pass a lambda of the form: '() => Class.Property' or '() => object.Property'"); 
    } 

    return me.Member.Name; 
} 

在此之後,我每次更改屬性的名稱時,我將不得不物業處處重命名我GetPropertyName而不是搜索的硬編碼的字符串值。

我也好奇內置的方式做的依賴,所以我把最喜歡在那裏:)

相關問題