假设我有一个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方法。
答案 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有某种未提及的特殊类型)。