绑定到BindingList的Current属性

时间:2013-07-09 15:36:01

标签: c# winforms data-binding bindinglist

假设我有一个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方法。

3 个答案:

答案 0 :(得分:2)

使用下面提供的NestedBindingProxy类,您可以执行

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

下面是NestedBindingProxy的c#代码。 WinForms数据绑定的问题是,当您使用包含多个属性的导航路径时,它不会检测值更改。虽然WPF做到了这一点。所以我创建了执行更改检测的类NestedBindingProxy,它公开了一个名为“Value”的属性,windows绑定也可以绑定它。每当导航路径中的任何属性发生更改时,将为“Value”属性触发notify属性更改事件。

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

}

答案 1 :(得分: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);

我没有测试过,但它应该可以工作,我一直在等待你的反馈。

答案 2 :(得分: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的原始代码不起作用,因为没有名为&#39; Current&#39;在BindingList上(除非OP有某种未提及的特殊类型)。

相关问题