Не редко можно встретить вопросы о том, как во время выполнения программы или в режиме разработки (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: «Тихо в лесу…»
Здравствуйте, _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 не скрывается
как можно это побороть?