1

假设我有一个BindingList<Person>wherePerson有一个名为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 方法。

4

3 回答 3

2

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

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

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

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

}

于 2013-07-11T12:17:13.640 回答
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);

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

于 2013-07-09T15:42:48.800 回答
0

我偶然发现了这一点(在原始帖子发布四年后),经过快速阅读,我发现就OP的问题而言,接受的答案似乎真的过度设计了。

我使用这种方法,并在此处发布了答案,以(希望)帮助遇到同样问题的其他人。

以下应该有效(如果实际上this.myBindingListSource实现了IBindingList

我只会创建一个绑定源来包装我的绑定列表(在我的表单/视图中) BindingSource bindingSource = new BindingSource(this.myBindingListSource, string.Empty);

然后像这样绑定到绑定源:
nameLabel.DataBindings.Add(new Binding("Text", bindingSource, "Name"));

我认为 OP 的原始代码不起作用,因为 BindingList 上没有名为“Current”的成员(除非 OP 有某种未提及的特殊类型)。

于 2017-07-25T10:43:08.733 回答