2013-07-09 126 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没有提到的某种专门类型)。