3

我在我的应用程序中实现了 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 target) 在 System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] 参数, CultureInfo 文化, Boolean skipVisibilityChecks) 在 System.Reflection.RuntimeMethodInfo.Invoke( Object obj、BindingFlags invokeAttr、Binder binder、Object[] 参数、CultureInfo 文化)
在 System.ComponentModel.ReflectEventDescriptor.AddEventHandler(Object 组件,委托值) 在 System.ComponentModel.ReflectPropertyDescriptor.AddValueChanged(Object 组件,EventHandler 处理程序) 在 System.Windows.Forms.BindToObject.CheckBinding() 在 System.Windows.Forms.Binding .SetListManager(BindingManagerBase bindingManagerBase) 在 System.Windows.Forms.ListManagerBindingsCollection.AddCore(Binding dataBinding) 在 System.Windows.Forms.BindingsCollection.Add(Binding binding) 在 System.Windows.Forms.BindingContext.UpdateBinding(BindingContext newBindingContext, Binding binding ) 在 System.Windows.Forms.Control.UpdateBindings()

如果我使用 ext 对象而不是 binder,则绑定有效。我错过了 ICustomTypeDescriptor 实现中的某些内容吗?

4

2 回答 2

1

您必须用自定义描述符包装原始描述符。这是代码:

#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
}
于 2013-12-25T18:27:59.837 回答
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;
}

我想让它继续下去,所以我会继续玩它。

于 2012-06-09T09:36:13.543 回答