2013-07-09 95 views
1

假設我有一個BindingList<Person>,其中Person具有名爲Name的公共字符串屬性。有沒有辦法有效(如果不是直接)綁定到Current屬性(它是一個Person對象),然後索引到Name屬性?綁定到綁定列表的當前屬性

我想象的綁定設置類似於

nameLabel.DataBindings.Add(
    new Binding("Text", this.myBindingListSource, "Current.Name", true)); 

nameLabel.DataBindings.Add(
    new Binding("Text", this.myBindingListSource.Current, "Name", true)); 

這兩種方法產生運行時錯誤。

目前,我只是訂閱BindingList的CurrentChanged事件並在那裏處理更新。這可以工作,但如果可能的話我更喜歡DataBinding方法。

回答

2

使用下面提供的NestedBindingProxy類,你可以做

nameLabel.DataBindings.Add(
new Binding("Text", new NestedBindingProxy(this.myBindingListSource, "Current.Name"), true)); 

下面是NestedBindingProxy C#代碼。 WinForms數據綁定的問題是,當您使用包含多個屬性的導航路徑時,它不檢測值更改。儘管WPF做到了這一點。所以我創建了NestedBindingProxy類來執行更改檢測,並且它暴露了一個名爲「Value」的屬性,Windows綁定可以綁定。每當導航路徑中有任何屬性發生更改時,通知屬性已更改事件將觸發「值」屬性。

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Threading; 

namespace WindowsFormsApplication4 
{ 
public sealed class NestedBindingProxy : INotifyPropertyChanged 
{ 
    class PropertyChangeListener 
    { 
     private readonly PropertyDescriptor _prop; 
     private readonly WeakReference _prevOb = new WeakReference(null); 

     public event EventHandler ValueChanged; 

     public PropertyChangeListener(PropertyDescriptor property) 
     { 
      _prop = property; 
     } 

     public object GetValue(object obj) 
     { 
      return _prop.GetValue(obj); 
     } 

     public void SubscribeToValueChange(object obj) 
     { 
      if (_prop.SupportsChangeEvents) 
      { 
       _prop.AddValueChanged(obj, ValueChanged); 
       _prevOb.Target = obj; 
      } 
     } 

     public void UnsubsctribeToValueChange() 
     { 
      var prevObj = _prevOb.Target; 
      if (prevObj != null) 
      { 
       _prop.RemoveValueChanged(prevObj, ValueChanged); 
       _prevOb.Target = null; 
      } 
     } 
    } 

    private readonly object _source; 
    private PropertyChangedEventHandler _subscribers; 
    private readonly List<PropertyChangeListener> _properties = new List<PropertyChangeListener>(); 
    private readonly SynchronizationContext _synchContext; 

    public event PropertyChangedEventHandler PropertyChanged 
    { 
     add 
     { 
      bool hadSubscribers = _subscribers != null; 
      _subscribers += value; 
      bool hasSubscribers = _subscribers != null; 
      if (!hadSubscribers && hasSubscribers) 
      { 
       ListenToPropertyChanges(true); 
      } 
     } 
     remove 
     { 
      bool hadSubscribers = _subscribers != null; 
      _subscribers -= value; 
      bool hasSubscribers = _subscribers != null; 
      if (hadSubscribers && !hasSubscribers) 
      { 
       ListenToPropertyChanges(false); 
      } 
     } 
    } 

    public NestedBindingProxy(object source, string nestedPropertyPath) 
    { 
     _synchContext = SynchronizationContext.Current; 
     _source = source; 
     var propNames = nestedPropertyPath.Split('.'); 
     Type type = source.GetType(); 
     foreach (var propName in propNames) 
     { 
      var prop = TypeDescriptor.GetProperties(type)[propName]; 
      var propChangeListener = new PropertyChangeListener(prop); 
      _properties.Add(propChangeListener); 
      propChangeListener.ValueChanged += (sender, e) => OnNestedPropertyChanged(propChangeListener); 
      type = prop.PropertyType; 
     } 
    } 

    public object Value 
    { 
     get 
     { 
      object value = _source; 
      foreach (var prop in _properties) 
      { 
       value = prop.GetValue(value); 
       if (value == null) 
       { 
        return null; 
       } 
      } 
      return value; 
     } 
    } 

    private void ListenToPropertyChanges(bool subscribe) 
    { 
     if (subscribe) 
     { 
      object value = _source; 
      foreach (var prop in _properties) 
      { 
       prop.SubscribeToValueChange(value); 
       value = prop.GetValue(value); 
       if (value == null) 
       { 
        return; 
       } 
      } 
     } 
     else 
     { 
      foreach (var prop in _properties) 
      { 
       prop.UnsubsctribeToValueChange(); 
      } 
     } 
    } 

    private void OnNestedPropertyChanged(PropertyChangeListener changedProperty) 
    { 
     ListenToPropertyChanges(false); 
     ListenToPropertyChanges(true); 
     var subscribers = _subscribers; 
     if (subscribers != null) 
     { 
      if (_synchContext != SynchronizationContext.Current) 
      { 
       _synchContext.Post(delegate { subscribers(this, new PropertyChangedEventArgs("Value")); }, null); 
      } 
      else 
      { 
       subscribers(this, new PropertyChangedEventArgs("Value")); 
      } 
     } 
    } 
} 

}

0

試試這個:

Binding bind = new Binding("Text", myBindingListSource, "Current"); 
bind.Format += (s,e) => { 
    e.Value = e.Value == null ? "" : ((Person)e.Value).Name; 
}; 
nameLabel.DataBindings.Add(bind); 

我沒有測試它,但它應該工作,我一直在等待您的反饋意見。

+0

它產生一個運行時錯誤,說我不能綁定到'Current'。我擔心我可能會試圖強制BindingList做一些它並不打算做的事情。謝謝你!我喜歡這個'Format'屬性。 – chessofnerd

+0

@chessofnerd您提到'BindingList',但'BindingList'沒有'Current'屬性,那麼如何綁定一個不存在的屬性?我以爲你談到了一個'BindingSource'。 –

0

我碰到這個偶然的事故(四年後原來的職位)和快速閱讀後,我發現,公認的答案看來是真的過度設計中對於OP的問題。

我使用這種方法,並在此發佈了一個答案(希望)可以幫助其他任何有相同問題的人。

下面應該工作(如果INFACT this.myBindingListSource器具IBindingList

我只想創造一個binding source來包裝我的綁定列表(在我的表/視圖) BindingSource bindingSource = new BindingSource(this.myBindingListSource, string.Empty);

然後就是綁定到綁定來源是這樣的:
nameLabel.DataBindings.Add(new Binding("Text", bindingSource, "Name"));

我覺得OP的原代碼沒有工作,因爲沒有所謂的「電流」上的BindingList成員(UN少一些OP沒有提到的某種專門類型)。