[C# 2.0] Динамическое управление набором свойств
От: _FRED_ Черногория
Дата: 28.03.07 08:15
Оценка: 33 (8) +1
Не редко можно встретить вопросы о том, как во время выполнения программы или в режиме разработки (design time) изменить набор свойств того или иного объекта. Вариантов решения может быть несколько. Здесь один из них. Сначала пример использования.
namespace WindowsApplication1
{
  using System;
  using System.ComponentModel;

  enum PropertyAttributes
  {
    Browsable,
    ReadOnly,
    NoBrowsable,
  }

  [TypeConverter(typeof(MyComponentTypeConverter))] // Свойства изменяются при помощи TypeConverter-а
  class MyComponent : Component
  {
    #region Fields

    //internal const string AttributesPropertyName = "Attributes";
    private const PropertyAttributes AttributesDefaultValue = PropertyAttributes.Browsable;
    private PropertyAttributes attributes = AttributesDefaultValue;

    internal const string ValuePropertyName = "Value";
    private const string ValueDefaultValue = "Some Value";
    private string property = ValueDefaultValue;

    #endregion Fields

    #region Properties

    [DefaultValue(AttributesDefaultValue)]
    [RefreshProperties(RefreshProperties.All)]
    public PropertyAttributes Attributes {
      get { return attributes; }
      set { attributes = value; }
    }

    [DefaultValue(ValueDefaultValue)]
    public string Value {
      get { return property; }
      set { property = value; }
    }

    #endregion Properties
  }

  // Вся работа делается в базовом классе
    // Наследник нужет только лишь для переопределения единственного абстрактного метода.
  class MyComponentTypeConverter : CustomPropertiesTypeConverter<MyComponent>
  {
    protected override Attribute[] GetPropertyAttributes(MyComponent component, PropertyDescriptor property) {
      if(property.Name == MyComponent.ValuePropertyName) {
        switch(component.Attributes) {
        case PropertyAttributes.Browsable:
          return new Attribute[] { BrowsableAttribute.Yes };
        case PropertyAttributes.ReadOnly:
          return new Attribute[] { ReadOnlyAttribute.Yes, };
        case PropertyAttributes.NoBrowsable:
          return new Attribute[] { BrowsableAttribute.No };
        }//switch
      }//if

      return EmptyAttributes; // Поле определено в базовом классе
    }
  }
}

Результат:
Вот исходники базового класса:
namespace WindowsApplication1
{
  #region Using's

  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.ComponentModel;
  using System.Diagnostics;

  #endregion Using's

  public abstract class CustomPropertiesTypeConverter<TComponent> : TypeConverter
  {
    #region Fields

    protected static readonly Attribute[] EmptyAttributes = new Attribute[0];

    #endregion Fields

    #region Constructors\Finalizer

    protected CustomPropertiesTypeConverter() {
    }

    #endregion Constructors\Finalizer

    #region Methods

    protected abstract Attribute[] GetPropertyAttributes(TComponent component, PropertyDescriptor property);

    #endregion Methods

    #region Overrides

    public override bool GetPropertiesSupported(ITypeDescriptorContext context) {
      return true;
    }

    public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] filter) {
      if(value != null) {
        TComponent componet = (TComponent)value;
        PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value, true);
        List<PropertyDescriptor> descriptors = new List<PropertyDescriptor>(properties.Count);
        foreach(PropertyDescriptor property in properties) {
          Attribute[] propertyAttributes = GetPropertyAttributes(componet, property);
          AttributeCollection attributeCollection = AttributeCollection.FromExisting(property.Attributes, propertyAttributes);
          if(attributeCollection.Contains(filter)) {
            Attribute[] attrs = new Attribute[attributeCollection.Count];
            attributeCollection.CopyTo(attrs, 0);
            descriptors.Add(new CustomPropertyDescriptor(property, attrs));
          }//if
        }//for
        return new PropertyDescriptorCollection(descriptors.ToArray(), ((IList)properties).IsReadOnly);
      } else {
      }//if
      return TypeDescriptor.GetProperties(typeof(TComponent), filter, true);
    }

    #endregion Overrides

    /// <summary>
    /// Необходим для настройки (замены) аттрибутов свойства.
    /// </summary>
    private sealed class CustomPropertyDescriptor : SimplePropertyDescriptor
    {
      #region Fields

      private readonly PropertyDescriptor inner;

      #endregion Fields

      #region Constructors\Finalizer

      public CustomPropertyDescriptor(PropertyDescriptor inner, Attribute[] attributes)
        : base(inner != null ? inner.ComponentType : null, 
               inner != null ? inner.Name : null, 
               inner != null ? inner.PropertyType : null, 
               attributes) 
      {
        if(inner == null) {
          throw new ArgumentNullException("inner");
        }//if

        this.inner = inner;
      }

      #endregion Constructors\Finalizer

      #region Properties

      private PropertyDescriptor InnerPropertyDescriptor {
        [DebuggerStepThrough]
        get { return inner; }
      }

      #endregion Properties

      #region Overrides

      public override object GetValue(object component) {
        return InnerPropertyDescriptor.GetValue(component);
      }

      public override void SetValue(object component, object value) {
        InnerPropertyDescriptor.SetValue(component, value);
      }

      #endregion Overrides
    }
  }
}
... << RSDN@Home 1.2.0 alpha rev. 675>>
Now playing: «Тихо в лесу…»
Help will always be given at Hogwarts to those who ask for it.
Отредактировано 04.01.2018 15:11 _FRED_ . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.