2013-07-09 2 views
1

Скажите, что у меня есть BindingList<Person>, где Person имеет общедоступное свойство строки, называемое Name. Есть ли способ эффективно (если не напрямую) привязываться к свойству Current (который является объектом Person), а затем индексировать его в свойство Name?Связывание с текущей недвижимостью BindingList

Я воображал привязка создана как

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)); 

Ниже C# код NestedBindingProxy. Проблема с привязкой данных WinForms заключается в том, что она не обнаруживает изменения значений при использовании пути навигации, который содержит несколько свойств. WPF делает это, хотя. Поэтому я создал класс NestedBindingProxy, который выполняет обнаружение изменений, и предоставляет свойство «Значение», которое связывание окон также может связывать. Всякий раз, когда какое-либо свойство изменяется в пути навигации, событие с изменением свойства уведомления активируется для свойства «Значение».

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 делать то, что он не собирается делать. Однако, спасибо! Мне нравится это свойство «Формат». – chessofnerd

+0

@chessofnerd вы говорили о 'BindingList', но' BindingList' не имеет свойства 'Current', так как вы можете связать несуществующее свойство? Я думал, вы говорили о «BindingSource». –

0

я наткнулся на это случайно (через четыре года после оригинального поста), и после быстрого чтения, я считаю, что принятый ответ, кажется, действительно избыточны по отношению к ОП.

Я использую этот подход и разместил здесь ответ (надеюсь) помочь кому-либо, у кого есть такая же проблема.

Следующие должен работать (если Infact this.myBindingListSource реализует IBindingList)

Я бы просто создать binding source, чтобы обернуть мой связывающий список (в моей форме/View) BindingSource bindingSource = new BindingSource(this.myBindingListSource, string.Empty);

А потом просто привязать к связыванию источник, как это:
nameLabel.DataBindings.Add(new Binding("Text", bindingSource, "Name"));

Я думаю, что исходный код в OP не работала, потому что нет ни одного члена под названием «Current» на BindingList (ип менее ОП имеет какой-то специализированный тип, не упомянутый).

Смежные вопросы