我有以下类(class)...
class ExpressionBinder<T>
{
public Func<T> Getter;
public Action<T> Setter;
public T Value
{
get { return Getter.Invoke(); }
set { Setter.Invoke(value); }
}
public ExpressionBinder(Func<T> getter, Action<T> setter)
{
Getter = getter;
Setter = setter;
}
}
class ComparisonBinder<TSource, TValue> : ExpressionBinder<bool>, INotifyPropertyChanged
{
private TSource instance;
private TValue comparisonValue;
private PropertyInfo pInfo;
public event PropertyChangedEventHandler PropertyChanged;
public ComparisonBinder(TSource instance, Expression<Func<TSource, TValue>> property, TValue comparisonValue) : base(null,null)
{
pInfo = GetPropertyInfo(property);
this.instance = instance;
this.comparisonValue = comparisonValue;
Getter = GetValue;
Setter = SetValue;
}
private bool GetValue()
{
return comparisonValue.Equals(pInfo.GetValue(instance, null));
}
private void SetValue(bool value)
{
if (value)
{
pInfo.SetValue(instance, comparisonValue,null);
NotifyPropertyChanged("CustomerName");
}
}
private void NotifyPropertyChanged(string pName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(pName));
}
}
/// <summary>
/// Adapted from surfen's answer (https://stackoverflow.com/a/10003320/219838)
/// </summary>
private PropertyInfo GetPropertyInfo(Expression<Func<TSource, TValue>> propertyLambda)
{
Type type = typeof(TSource);
MemberExpression member = propertyLambda.Body as MemberExpression;
if (member == null)
throw new ArgumentException(string.Format(
"Expression '{0}' refers to a method, not a property.",
propertyLambda));
PropertyInfo propInfo = member.Member as PropertyInfo;
if (propInfo == null)
throw new ArgumentException(string.Format(
"Expression '{0}' refers to a field, not a property.",
propertyLambda));
if (type != propInfo.ReflectedType &&
!type.IsSubclassOf(propInfo.ReflectedType))
throw new ArgumentException(string.Format(
"Expresion '{0}' refers to a property that is not from type {1}.",
propertyLambda,
type));
return propInfo;
}
}
我使用 ComparisonBinder
类的代码如下:
radioMale.DataBindings.Add(
"Checked",
new ComparisonBinder<DataClass, GenderEnum>(DataObj, (x) => x.Gender, GenderEnum.Male),
"Value",
false,
DataSourceUpdateMode.OnPropertyChanged);
radioFemale.DataBindings.Add(
"Checked",
new ComparisonBinder<DataClass, GenderEnum>(DataObj, (x) => x.Gender, GenderEnum.Male),
"Value",
false,
DataSourceUpdateMode.OnPropertyChanged);
它们允许我使用表达式将许多控件绑定(bind)到同一个属性,从而为控件的绑定(bind)属性生成值。从控件到对象,它都工作得很漂亮。 (如果您想了解更多关于使用此类和 this question 的信息)
现在,我需要换个方向。当我更新我的对象(这将改变 get 表达式的结果)时,我需要更新绑定(bind)控件。我尝试实现 INotifyPropertyChanged
但它没有任何区别。
一件奇怪的事:将控件 DataSourceUpdateMode
设置为 OnPropertyChanged
以某种方式阻止我更改选中的 radio 。
最佳答案
快速回答:
- 您必须使用绑定(bind)源
- 在您的对象中实现 INotifyPropertyChanged
- 让代理监听上面的事件
- 在代理中实现 INotifyPropertyChanged
- 在代理中过滤从您的对象收到的 PropertyChanged 事件,并仅在
((PropertyChangedEventArgs) args).PropertyName == pInfo.Name
时引发
- 将代理添加到
BindSource.DataSource
- 使用
BindingSource
将控件绑定(bind)到数据对象的属性
最终代码
可用(连同其他绑定(bind)类)位于:http://github.com/svallory/BindingTools
类
class ExpressionBinder<T> : INotifyPropertyChanged
{
public Func<T> Getter;
public Action<T> Setter;
public event PropertyChangedEventHandler PropertyChanged;
protected IList<string> WatchingProps;
public T Value
{
get { return Getter.Invoke(); }
set { Setter.Invoke(value); }
}
public ExpressionBinder(Func<T> getter, Action<T> setter)
{
WatchingProps = new List<string>();
Getter = getter;
Setter = setter;
}
public ExpressionBinder(Func<T> getter, Action<T> setter, ref PropertyChangedEventHandler listenToChanges, IList<string> propertyNames)
{
Getter = getter;
Setter = setter;
listenToChanges += SourcePropertyChanged;
WatchingProps = propertyNames;
}
protected void SourcePropertyChanged(object obj, PropertyChangedEventArgs args)
{
if (PropertyChanged != null && WatchingProps.Contains(args.PropertyName))
{
PropertyChanged(this, args);
}
}
protected PropertyInfo GetPropertyInfo<TSource, TValue>(Expression<Func<TSource, TValue>> propertyLambda)
{
Type type = typeof(TSource);
var member = propertyLambda.Body as MemberExpression;
if (member == null)
throw new ArgumentException(string.Format(
"Expression '{0}' refers to a method, not a property.",
propertyLambda));
var propInfo = member.Member as PropertyInfo;
if (propInfo == null)
throw new ArgumentException(string.Format(
"Expression '{0}' refers to a field, not a property.",
propertyLambda));
if (type != propInfo.ReflectedType &&
!type.IsSubclassOf(propInfo.ReflectedType))
throw new ArgumentException(string.Format(
"Expresion '{0}' refers to a property that is not from type {1}.",
propertyLambda,
type));
return propInfo;
}
}
class ComparisonBinder<TSource, TValue> : ExpressionBinder<bool> where TSource : INotifyPropertyChanged
{
private readonly TSource instance;
private readonly TValue comparisonValue;
private readonly PropertyInfo pInfo;
public ComparisonBinder(TSource instance, Expression<Func<TSource, TValue>> property, TValue comparisonValue)
: base(null, null)
{
pInfo = GetPropertyInfo(property);
this.instance = instance;
this.comparisonValue = comparisonValue;
Getter = GetValue;
Setter = SetValue;
instance.PropertyChanged += SourcePropertyChanged;
WatchingProps.Add(pInfo.Name);
}
private bool GetValue()
{
return comparisonValue.Equals(pInfo.GetValue(instance, null));
}
private void SetValue(bool value)
{
if (value)
{
pInfo.SetValue(instance, comparisonValue, null);
}
}
}
用法
var bs = new BindingSource();
bs.DataSource = new ComparisonBinder<MySourceObjClass, PropType>(
MySourceObject,
p => p.PropertyToBind,
ValueToCompare);
rdBeSmart.DataBindings.Add(
"Checked",
bs,
"Value");
关于c# - 如何创建双向数据绑定(bind)代理?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10004127/