[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_ . Предыдущая версия .
Re: [C# 2.0] Динамическое управление набором свойств
От: vehfl  
Дата: 26.08.09 13:50
Оценка:
Здравствуйте, _FRED_:

немного модифицировал ваш код под свои нужды:

   [TypeConverter(typeof(DirectionTypeConverter))]
    public class Direction : Component
    {
        ....
        DateTime registrationDate;
        DateTime removeFromControlDate;
        bool onPost = false;

        ....
        [Browsable(false)]
        public bool OnPost
        {
            get { return onPost; }
            set { onPost = value; }
        }

        [DisplayName("Время обработки")]
        [Description("Время обработки направления на таможне. Отсчет ведется от даты регистрации направления на таможне.")]
        public string TimeOfProcessing
        {
            get
            {
                string result = "";
                TimeSpan ts;
                if (removeFromControlDate == new DateTime())
                    ts = DateTime.Now - registrationDate;
                else
                    ts = removeFromControlDate - registrationDate;
                if (ts.Days > 0) result = ts.Days.ToString() + " д. ";
                return result + ts.Hours.ToString() + " час. " + ts.Minutes.ToString() + " мин.";
            }
        }
    }

    class DirectionTypeConverter : CustomPropertiesTypeConverter<Direction>
    {
        protected override Attribute[] GetPropertyAttributes(Direction component, PropertyDescriptor property)
        {
            if (property.Name == "TimeOfProcessing")
            {
                // для проверки попадаю ли я сюда
                System.Windows.Forms.MessageBox.Show(component.OnPost.ToString());
                if (component.OnPost)
                {
                    return new Attribute[] { BrowsableAttribute.No };
                }
                else
                {
                    return new Attribute[] { BrowsableAttribute.Yes };
                }
            }
            return EmptyAttributes;
        }
    }

класс CustomPropertiesTypeConverter остался без изменений.... при созданни класса Direction свойтству OnPost присваиваю true...

когда запускаю из под VisualStudio 2005 все нормально: появляется сообщние "True" и свойство TimeOfProcessing скрывается...
все это скомпилирован в firstdll...
есть другое приложение на С# seconddll котороя тоже представляет из себя dll... она использует firstdll... из под VS2005 все работает: появляется сообщние "True" и свойство TimeOfProcessing скрывается...

проблема заключается в следующем: seconddll регистрирую как com... запускаю приложение написанное на VB, которая в свою очередь вызывает этот com (seconddll) использующая firstdll. Но при этом свойство TimeOfProcessing не скрывается(((, сообщние тоже не появляется...

версии файлов сверены и совпадают

еще написал тестовое консольное приложение на C# все также работает нормально: появляется сообщние "True" и свойство TimeOfProcessing скрывается...

чтобы убедиться что свойство OnPost равно True, такой код
            propertyGrid.SelectedObject = e.Node.Tag;
            MessageBox.Show((e.Node.Tag as Direction).OnPost.ToString());

во всех случаях получаю сообщение с текстом True, в том и числе и в случае когда свойство TimeOfProcessing не скрывается

как можно это побороть?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.