Оболочка ICustomTypeDescriptor над объектом - PullRequest
3 голосов
/ 24 февраля 2012

Я реализовал 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 [] параметры, КультураИнфо-культура, логическое скипVisibilityChecks) в System.Reflection.RuntimeMethodInfo.Invoke (Object obj, BindingFlags invokeAttr, Binder Binder, Object [] параметры, CultureInfo culture)
at System.ComponentModel.ReflectEventDescriptor.AddEventHandler (Object компонент, значение делегата) в System.ComponentModel.ReflectPropertyDescriptor.AddValueChanged (Объект компонент, обработчик 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 обязательный) в System.Windows.Forms.BindingContext.UpdateBinding (BindingContext newBindingContext, Binding привязка) в System.Windows.Forms.Control.UpdateBindings ()

Привязка работает, если вместо связующего я использую объект ext. Я что-то пропустил в реализации ICustomTypeDescriptor?

Ответы [ 2 ]

0 голосов
/ 25 декабря 2013

Вы должны обернуть оригинальные дескрипторы пользовательскими. Вот код:

#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
}
0 голосов
/ 09 июня 2012

Мне удалось воспроизвести вашу проблему в моем тестовом коде. Я вижу, что если вы не реализуете INotifyPropertyChanged в ExtensionModel, то это работает!

Итак, в вашей реализации 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;
}

Я бы хотел, чтобы это продолжалось, поэтому я продолжу играть с ним.

...