我在我的应用程序中实现了ICustomTypeDescriptor,以便能够在运行时定义自定义属性。我的基本实现如下:
public class DynamicClass <T> : ICustomTypeDescriptor
{
private readonly T _object;
public DynamicClass(T trackedObject)
{
_object = trackedObject;
}
// Collection to code add dynamic properties
public KeyedCollection<string, DynamicProperty> Properties
{
get;
private set;
}
// ICustomTypeDescriptor implementation
public AttributeCollection GetAttributes()
{
return TypeDescriptor.GetAttributes(_object, true);
}
public string GetClassName()
{
return TypeDescriptor.GetClassName(_object, true);
}
public string GetComponentName()
{
return TypeDescriptor.GetComponentName(_object, true);
}
public TypeConverter GetConverter()
{
return TypeDescriptor.GetConverter(_object, true);
}
public EventDescriptor GetDefaultEvent()
{
return TypeDescriptor.GetDefaultEvent(_object, true);
}
public PropertyDescriptor GetDefaultProperty()
{
return TypeDescriptor.GetDefaultProperty(_object, true);
}
public object GetEditor(Type editorBaseType)
{
throw new NotImplementedException();
}
public EventDescriptorCollection GetEvents()
{
return TypeDescriptor.GetEvents(_object, true);
}
public EventDescriptorCollection GetEvents(Attribute[] attributes)
{
return TypeDescriptor.GetEvents(_object, attributes, true);
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
{
return TypeDescriptor.GetProperties(_object, true);
}
public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
return TypeDescriptor.GetProperties(_object, attributes, true);
}
public object GetPropertyOwner(PropertyDescriptor pd)
{
return _object;
}
}
问题是,现在当我使用DynamicClass绑定器将对象绑定到文本框时,它不再起作用了。
我这样用:
DynamicClass<ExtensionModel> binder = new DynamicClass<ExtensionModel>(ext);
_versionLabel.DataBindings.Add("Text", binder, "SelectedVersion", false, DataSourceUpdateMode.OnPropertyChanged);
我得到了例外:'对象与目标类型不匹配。'
对象与目标类型不匹配。
在System.Reflection.RuntimeMethodInfo.CheckConsistency(Object 目标)在System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr,Binder binder,Object []参数, CultureInfo culture,Boolean skipVisibilityChecks)at System.Reflection.RuntimeMethodInfo.Invoke(Object obj,BindingFlags invokeAttr,Binder binder,Object []参数,CultureInfo文化)
在System.ComponentModel.ReflectEventDescriptor.AddEventHandler(Object 组件,委托值) System.ComponentModel.ReflectPropertyDescriptor.AddValueChanged(对象 组件,EventHandler处理程序)at System.Windows.Forms.BindToObject.CheckBinding()at System.Windows.Forms.Binding.SetListManager(BindingManagerBase bindingManagerBase)at System.Windows.Forms.ListManagerBindingsCollection.AddCore(绑定 System.Windows.Forms.BindingsCollection.Add(Binding。)中的dataBinding 绑定) System.Windows.Forms.BindingContext.UpdateBinding(BindingContext中 newBindingContext,Binding binding)at System.Windows.Forms.Control.UpdateBindings()
如果使用ext对象而不是绑定器,则绑定有效。我在ICustomTypeDescriptor实现中遗漏了什么吗?
答案 0 :(得分:0)
我已设法在我的测试代码中重现您的问题。我可以看到,如果你没有在ExtensionModel上实现INotifyPropertyChanged那么它可以工作!
因此,您的ICustomTypeDescriptor实现与实现INotifyPropertyChanged的属性类无关。
这样可行,但如果您取消注释INotifyPropertyChange,它将会中断。
public class BindingExample
{
public void Shows_Binding_To_A_Label_With_DynamicClass()
{
Form frm = new Form();
Label _versionLabel = new Label();
frm.Controls.Add(_versionLabel);
ExtensionModel ext = new ExtensionModel() { SelectedVersion = "DynamicClass Example" };
DynamicClass<ExtensionModel> binder = new DynamicClass<ExtensionModel>(ext);
_versionLabel.DataBindings.Add("Text", binder, "SelectedVersion", false, DataSourceUpdateMode.OnPropertyChanged);
frm.ShowDialog();
}
}
public class ExtensionModel// : INotifyPropertyChanged
{
string selectedVersion;
public string SelectedVersion
{
get { return selectedVersion; }
set
{
selectedVersion = value;
onPropertyChanged("SelectedVersion");
}
}
void onPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
我想这样做,所以我会继续玩它。
答案 1 :(得分:0)
您必须使用自定义描述符包装原始描述符。这是代码:
#region IBindingProxy
public interface IBindingProxy
{
void CheckAndNotify();
}
#endregion
public class BindingProxy : CustomTypeDescriptor, INotifyPropertyChanged, IBindingProxy
{
#region Static Constructor
private static readonly IDictionary<Type, PropertyDescriptorCollection> propertyCache;
static BindingProxy()
{
propertyCache = new Dictionary<Type, PropertyDescriptorCollection>();
}
private static PropertyDescriptorCollection GetTypeProperties(Type type)
{
lock (propertyCache)
{
PropertyDescriptorCollection properties;
if (!propertyCache.TryGetValue(type, out properties))
{
var list = new List<PropertyDescriptor>();
GetTypeProperties(list, type);
properties = new PropertyDescriptorCollection(list.ToArray());
propertyCache.Add(type, properties);
}
return properties;
}
}
private static void GetTypeProperties(ICollection<PropertyDescriptor> list, Type type)
{
foreach (var @interface in type.GetInterfaces())
{
GetTypeProperties(list, @interface);
}
foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(type))
{
list.Add(new ProxyPropertyDescriptor(property));
}
}
#endregion
private readonly PropertyDescriptorCollection properties;
private readonly object instance;
public event PropertyChangedEventHandler PropertyChanged;
public BindingProxy(object instance)
{
this.instance = instance;
properties = instance == null
? PropertyDescriptorCollection .Empty
: GetTypeProperties(instance.GetType());
}
public void CheckAndNotify()
{
OnPropertyChanged(null);
}
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public object Instance
{
get { return instance; }
}
public override PropertyDescriptorCollection GetProperties()
{
return GetProperties(null);
}
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
return properties;
}
public override Object GetPropertyOwner(PropertyDescriptor property)
{
return this;
}
#region ProxyPropertyDescriptor
private class ProxyPropertyDescriptor : PropertyDescriptor
{
private readonly PropertyDescriptor property;
public ProxyPropertyDescriptor(PropertyDescriptor property) : base(property)
{
this.property = property;
}
//public override string DisplayName
//{
// get { return property.DisplayName; }
//}
//public override string Description
//{
// get { return property.Description; }
//}
//public override string Category
//{
// get { return property.Category; }
//}
//public override TypeConverter Converter
//{
// get { return converter; }
//}
public override bool IsReadOnly
{
get { return property.IsReadOnly; }
}
public override void ResetValue(object component)
{
}
public override bool CanResetValue(object component)
{
return false;
}
public override bool ShouldSerializeValue(object component)
{
return false;
}
public override Type ComponentType
{
get { return property.ComponentType; }
}
public override Type PropertyType
{
get { return property.PropertyType; }
}
public override object GetValue(object component)
{
return property.GetValue(((BindingProxy)component).instance);
}
public override void SetValue(object component, object value)
{
var instance = ((BindingProxy)component).instance;
property.SetValue(instance, value);
OnValueChanged(instance, EventArgs.Empty);
}
}
#endregion
}