ICustomTypeDescriptor包装在一个对象上

时间:2012-02-24 08:28:26

标签: .net winforms data-binding icustomtypedescriptor

我在我的应用程序中实现了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实现中遗漏了什么吗?

2 个答案:

答案 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
}