繁体   English   中英

绑定到BindingList的Current属性

[英]Binding to the Current property of a 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方法。

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

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

以下是NestedBindingProxy的C#代码。 WinForms数据绑定的问题在于,当您使用包含多个属性的导航路径时,它不会检测到值更改。 WPF可以做到这一点。 因此,我创建了进行更改检测的NestedBindingProxy类,它公开了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"));
            }
        }
    }
}

}

尝试这个:

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

我尚未测试过,但它应该可以工作,我一直在等待您的反馈。

我偶然发现了这个问题(在原始帖子发布四年后),并且在快速阅读之后,我发现对于OP的问题,被接受的答案似乎设计得过分。

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

以下应该工作(如果this.myBindingListSource实现IBindingList

我只需要创建一个绑定源即可包装我的绑定列表(在我的Form / View中) BindingSource bindingSource = new BindingSource(this.myBindingListSource, string.Empty);

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM