Главная » C#, Компоненты » Динамическое управление набором свойств

0

В разд. 6.6 я показывал, как скрыть элементы из редактора свойств, а в разд. 5.4 рассказывал об атрибуте Browsabie. Но это было скрытие "насовсем", т. е. свойство просто не видно в режиме разработки. Сейчас я хочу показать, как сделать динамическое управление свойствами, когда некоторые свойства будут видны или скрыты в зависимости от значений других свойств. Добавим в наш компонент градиентной заливки свойство Gradient Active. Если оно имеет значение true, компонент показывает градиентную заливку, а если false, то работает как обычный компонент Label. Конечно, практического смысла это свойство не имеет, но в качестве примера вполне подходит. Соответствующий код показан в листинге 8.13.

Листинг 8.13 Компонент i»j ridi«nLU\i>=i со свойством bradiontActivo

using   System;

® using  System.Collections.Generic;

! using  System.ComponentModel;

using   System.Drawing;

using   System. Data;

using   System.Text;

using   System.Windows.Forms;

namespace MyControl (

[Toolboxltem(true)]

[ToolboxBitmap(typeof(GradientLabel), "GradientLabel.bmp")] [DesignerAttribute(typeof(GradientLabelDesigner))] [TypeConverter(typeof(GradientLabelTypeConverter))J public partial class GradientLabel : Label

{

private Color startColor = Color.LightGreen; private Color endColor = Color.DarkBlue; private bool gradientActive = true;

/// <suirunary>

/// true, если заливка включена III </summary>

[CategoryC’Gradient") , Description("rpaflMeHT включен") , DefaultValue(typeof(bool), "true")] [RefreshProperties(RefreshProperties.All)]

public bool GradientActive 1

get { return gradientActive; }

set {

gradientActive = value; OnChangeProperties{);

}

/// <summary>

/// Цвет начала заливки

/// </suinmary>

(Category("Gradient"), Description("Цвет начала заливки"), DefaultValue(typeof(Color), "LightGreen"), AmbientValue(typeof(Color), "Empty")] public Color StartColor t

get 1

if {startColor — Color.Empty)

return Parent.BackColor; return startColor;

set 1

startColor = value; OnChangeProperties();

}

}

/// <summary>

/// Цвет завершения заливки III </summary>

[Category("Gradient"), Description("цвет завершения заливки"), DefaultValue(typeof(Color), "DarkBlue"}] public Color EndColor 1

get { return endColor; } set 1

endColor = value; OnChangeProperties{};

/// <summary>

/// Вызывается при изменении свойства заливки /// </summary>

private void OnChangeProperties(} Invalidate{);

}

/// <summary>

/// Отрисовка компонента

/// </summary>

/// <param name="pe"X/param>

protected override void OnPaint{PaintEventArgs pe}

// Вызываем базовый OnPaint base.OnPaint(pe);

II Заливаем, если заливка включена i f (gradientAct ive)

Color cl = Color.FromArgb{100, startColor); Color c2 = Color.FromArgb(100, endColor); Brush b = new Drawing2D.LinearGradientBrush(

ClientRectangle, cl, c2, 10}; pe.Graphics.FillRectangle(b, ClientRectangle}; b.Dispose();

}

}

/// <summary> III Конструктор III </summary> public GradientLabel()

InitializeComponent();

}

1

Итак, наша задача формулируется следующим образом: не показывать свойства StartColor и EndColor в редакторе свойств, если значение GradientActive равно false.

В предыдущем разделе мы подключали конвертер типа к конкретному свойству, а сейчас мы подключим его ко всему компоненту и, таким образом, будем работать со свойствами всего компонента (или, правильнее сказать, конвертер подключается к типу GradientLabei и применяется ко всем его экземплярам):

[TypeConverter{typeof(GradientLabelTypeConverter))] public partial class GradientLabei : Label

Про метод GetPropertiesSupportedO я уже рассказывал (см. разд. 8.4). В нашем случае он всегда будет возвращать true.

Вся основная работа будет идти в методе GetPropertieso, который возвращает коллекцию дескрипторов свойств компонента. Еще раз напомню его описание:

public override PropertyDescriptorCollection GetProperties! ITypeDescriptorContext context, object value, Attribute [] attributes)

{ )

Параметр attributes задает фильтр, согласно которому выбираются свойства компонента. Обычно редактор свойств запрашивает свойства с атрибутом Browsabie.Yes, т. е. только те, которые были предназначены для отображения. Получить нужную коллекцию можно с помощью метода

TypeDescriptor.GetProperties{):

PropertyDescriptorCollection coll = // без учета фильтра TypeDescriptor.GetProperties(value.GetType()); PropertyDescriptorCollection coll -// с учетом фильтра

TypeDescriptor.GetProperties(value.GetType(), attributes);

Для того чтобы метод GetPropertieso вызывался при изменении значения свойства GradientActive, это свойство должно быть отмечено атрибутом RefreshProperties (RefreshProperties. All). Он говорит редактору свойств, что при изменении значения свойства нужно обновить все другие. Итак, нам надо таким образом модифицировать коллекцию coil, чтобы свойства startColor и EndColor не были видны в редакторе свойств в случае, когда GradientActive равно false. Все было бы просто, если бы не одна проблема: коллекция coll, которую мы получили, является коллекцией "только для чтения". Из нее нельзя удалять значения или менять их. Придется искать другой выход— переписывать эту коллекцию в другую, фильтруя значения самостоятельно. Конструктор класса PropertyDescriptorCollection имеет следующий вид:

public PropertyDescriptorCollection(

PropertyDescriptorf] properties, bool readonly)

Последний параметр задает режим "только для чтения". Его можно просто скопировать из существующей коллекции:

(coll as IList) . IsReadOnly

Теперь займемся созданием массива дескрипторов свойств (первый параметр конструктора). Общий алгоритм такой:

1.    Получить коллекцию свойств без учета фильтра, т. к. фильтровать свойства придется вручную.

2.    Создать массив для хранения свойств.

3.    Просмотреть каждое свойство исходной коллекции.

4.    Если надо, корректировать атрибуты свойства.

5.    Проверить фильтр (т. к. атрибут мог измениться после нашей корректировки).

6.    Если фильтр удовлетворяет условию, добавить свойство в выходной массив.

1. Создать PropertyDescriptorCoilection на основе полученного массива. Корректировка атрибутов будет довольно простой. Если свойство GradientActive равно false, ТО свойствам StartColor и EndColor добавляем атрибут Browsable.No, а иначе добавляем атрибут Browsable.Yes:

if ((property.Name == "StartColor") || (property.Name == "EndColor"))

}

if ((value as GradientLabel). GradientActive) attributeList.Add(BrowsableAttribute.Yes); else

attributeList.Add(BrowsableAttribute.No);

}

Для проверки фильтра используется метод Contains класса AttributeCol- lection:-

sAttributeCollection newCollection =

AttributeCollection.FromExisting(

property.Attributes, attributeArray); if (newCollection.Contains (attributes) )

I }

Код получившегося конвертера показан в листинге 8.14. Теперь, если выставить значение Gradient Active в false, свойства StartColor и EndColor будут скрыты.

Листинге 14 Конвертер типа скрывающим свойства StartColor и LndCoior в id виси мости от свойства Cixadi^niActive

using System;

using System.Collections.Generic; using System.Text; using System.ComponentModel; using System.Collections;

namespace MyControl {

class GradientLabelTypeConverter : TypeConverter

С

public override bool GetPropertiesSupported(

ITypeDescriptorContext context)

{

return true;

}

public override PropertyDescriptorCollection GetProperties{ ITypeDescriptorContext context, object value, Attribute [] attributes)

{

// Получаем исходный список свойств, игнорируя фильтр PropertyDescriptorCollection coll =

TypeDescriptor.GetProperties(value.GetType{));

// Новый список свойств

List<PropertyDescriptor> outputCollection =

new List<PropertyDescriptor>(};

// Просматриваем все свойства

foreach (PropertyDescriptor property in coll) {

// Копируем исходные атрибуты в новый массив List<Attribute> attributeList = new List<Attribute> ();

foreach {Attribute attribute in property.Attributes)

{

attributeList.Add(attribute);

// Если это нужные нам свойства, модифицируем атрибуты if {(property.Name = "StartColor") || (property.Name — "EndColor"))

{

if ((value as GradientLabel).GradientActive) attributeList.Add(BrowsableAttribute.Yes);

}

else

attributeList.Add(BrowsableAttribute.No);

}

)

// Новый массив атрибутов

Attributef] attributeArray = attributeList.ToArray();

// Создаем AttributeCollection для проверки фильтра AttributeCollection newCollection =

AttributeCollection.FromExisting{

property.Attributes, attributeArray);

// Проверяем фильтр

if (newCollection.Contains(attributes))

// Если фильтр пройден — добавляем свойство //в выходную коллекцию

outputCollection.Add(new CustomPropertyDescriptor( property, attributeArray));

}

}

// Возвращаем PropertyDescriptorCollection return new PropertyDescriptorCollection(

outputCollection.ToArray(), (coll as IList).IsReadOnly);

}

protected sealed class CustomPropertyDescriptor : SimplePropertyDescriptor

private readonly PropertyDescriptor property;

public CustomPropertyDescriptor(PropertyDescriptor property, Attribute[] attributes) : base(property.ComponentType, property.Name, propertу.PropertyType, a11ributes)

this.property = property;

private PropertyDescriptor PropertyDescriptor get { return property; )

public override object GetValue(object component) return PropertyDescriptor.GetValue(component);

public override void SetValue(object component, object value) PropertyDescriptor.SetValue(component, value);

}

}

}

Литература:

Агуров П. В. C#. Разработка компонентов в MS Visual Studio 2005/2008. – СПб.: БХВ-Петербург, 2008. — 480 е.: ил.

По теме:

  • Комментарии