Главная » C#, Компоненты » Стандартный набор значений

0

Для enum-типов в редакторе свойств отображается выпадающий список с перечислением всех возможных значений этого типа. Это происходит автоматически, т. к. редактор типа знает, что других значений быть не может. Для других типов, не имеющих определенного набора значений, можно реализовать стандартный набор значений (standard values).

Сделаем, например, для нашего свойства Gradient несколько заранее определенных значений. Это могут быть комбинации цветов наиболее часто используемых заливок. Для этого нужно перекрыть два метода в конвертере типа:

О метод bool GetStandardValuesSupported(ITypeDescriptorContext context) возвращает true, если конвертер типа имеет стандартный набор значений;

0  метод StandardValuesCollection GetStandardValues{ITypeDescriptorContext context) возвращает сам набор значений, представленный классом коллекции StandardValuesCollection.

Итак, первый метод у иас будет просто возвращать true, не обращая ии на что внимания:

public override bool GetStandardValuesSupported(

ITypeDescriptorContext context)

1          

return true;

}

Для реализации второго метода потребуется сам набор значений. Пусть ои будет состоять из трех элементов:

private ArrayList values;

7alues = new ArrayList {new string[] {

"LightGreen,DarkBlue", "White,Red", "White,Blue" }};

Конструктор класса StandardValuesCollection позволяет СКОИВертИрОВать icoiiection в объект этого класса:

TypeConverter.StandardValuesCollection svc =

new TypeConverter.StandardValuesCollection(values);

Получившийся код конвертера показан в листинге 8.8.

^дстинг 8 8 Класс конвертера типа GradientEaramatars с двумя свойствами

using System;

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

namespace MyControl

// Этот класс позволяет отображать тип GradientParameters в строку

class GradientConverter : TypeConverter {

private ArrayList values;

public GradientConverter{) {

// Инициализируем набор значений values = new ArrayList(new string[] {

"LightGreen,DarkBlue", "White,Red", "White,Blue" ));

}

// Перекрываем метод CanConvertFrom.

// Интерфейс ITypeDescriptorContext передает контекст преобразования, public override bool CanConvertFrom(

ITypeDescriptorContext context, Type sourceType)

{

// Разрешаем преобразовьгвать string

if (sourceType == typeof(string)) {

return true;

}

// Вызываем базовый метод

return base.CanConvertFrom(context, sourceType);

}

// Перекрываем метод ConvertFrom. Производим преобразование

// в пользовательский тип.

public override object ConvertFrom(

ITypeDescriptorContext context, Culturelnfo culture, object value)

{

// Преобразовываем строку в GradientParameters

if {value is string} {

string svalue = value as string; return GradientParameters.Parse(svalue);

}

return base.ConvertFrom(context, culture, value);

}

// Перекрываем метод ConvertTo. Производим преобразование // из пользовательского типа.

public override object ConvertTo(ITypeDescriptorContext context,

Culturelnfo culture, object value, Type destinationType)

{

11 Преобразовываем GradientParameters в строку if (destinationType == typeof(string))

{

if (value != null) {

if (value is GradientParameters) {

return ((GradientParameters) value) . ToStringO;

}

else {

return value;

}

}

}

return base.ConvertTo(

context, culture, value, destinationType);

}

// Возвращаем true, если нужно отображать свойства класса public override bool GetPropertiesSupported(

ITypeDescriptorContext context)

{

return true;

}

// Возвращаем коллекцию свойств, которые нужно отображать public override PropertyDescriptorCollection GetProperties( ITypeDescriptorContext context, object value. Attribute[] attributes)

{

return TypeDescriptor.GetProperties(

typeof(GradientParameters));

}

// Возвращаем true, означающее, что тип поддерживает // стандартный набор значений

public override bool GetStandardValuesSupported(

ITypeDescriptorContext context)

{

return true;

}

// Возвращаем набор значений как StandardValuesCollection public override TypeConverter.StandardValuesCollection Get S t anda rdValue s(

ITypeDescriptorContext context)

{

TypeConverter.StandardValuesCollection svc =

new TypeConverter.StandardValuesCollection(values); return svc;

}

}

}

Алгоритм получения стандартных значений примерно такой:

1.         Редактор свойств вызывает метод GetPropertiesSupported о и узнает, что свойство должно показываться с набором стандартных значений.

2.         Вызывается метод Getstandardvaiues (), который возвращает коллекцию стандартных значений.

3.         Для каждого значения вызывается метод ConvertFrom(}, возвращающий объект нужного типа.

4.         Для кажДОГО Объекта ВЫЗЫВаетСЯ метОД ConvertTo() (ИЛИ ToStringO), возвращающий строковое представление объекта.

5.    Набор строк показывается в редакторе свойств.

Таким образом, независимо от того, какие значения записаны в исходной коллекции, в редактор свойств попадут значения, прошедшие "цикл конвертирования". Кстати, если в методах ConvertFrom (} или convertTo{) возникнет исключение, то Visual Studio просто "падает" и закрывается.

Обратите внимание, что метод convertFrom}) вызывает метод parse, который создает новый объект параметров:

public static GradientParameters Parse{string value)

{

string[] v = {{string}value}.Split(new char[] {             });

return new GradientParameters(

Color.FromName{v[0]}, Color.FromName (v [ 1 ] }) ;

}

Именно этот созданный объект будет присваиваться полю Gradient нашего компонента при выборе одного из стандартных значений, а значит, делегат, привязанный к объекту для уведомления об изменении полей Gradient, потеряется. Самый простой выход из положения— просто отказаться от делегата И вызывать метод GradientPropertyChanged прямо В блоке set свойства Gradient (листинг 8.9).

На этом создание конвертера типа закончено. Результат работы нового конвертера показан на рис. 8.6.

public partial class GradientLabel : Label

}

GradientParameters gradient =

new GradientParameters(Color.LightGreen, Color.DarkBlue);

[Category("Gradient")]

[Description("Свойства градиентной заливки")] [TypeConverter(typeof(GradientConverter))]

public GradientParameters Gradient

{

get { return gradient; } set {

gradient = value; GradientPropertyChanged();

}

}

// Обработчик изменения свойств градиентной заливки void GradientPropertyChanged()

}

// Перерисовать компонент Invalidate();

}

}

Для полноты картины замечу, что кроме описанных двух методов, у конвертера есть еще метод GetstandardvaiuesExciusiveo. Если он возвращает true, то ручное редактирование значения свойства не доступно, соответственно, значения можно выбирать только из предложенного списка:

public override bool GetStandardValuesExclusive {

ITypeDescriptorContext context)

{

.1

| )

return true;

Рис. 8.6. Стандартный набор для свойства ‘.^.к:: ont г dimeters

К сожалению, история на этом не заканчивается. В Visual Studio есть небольшая, но неприятная проблема. Если после выбора стандартного значения сделать двойной щелчок на поле редактирования, Visual Studio выводит сообщение об ошибке (рис. 8.7).

Рис. 8.7. Сообщение об ошибке при двойном щелчке

Это происходит из-за того, что при двойном щелчке среда пытается записать в поле Gradient строковое значение, что, конечно же, не удается. Побороть эту проблему можно, хотя и не очень красиво. Нужно создать "теневое" свойство, "отражающее" существующее, но имеющее тип object, и именно его

показывать в редакторе свойств. А правильное свойство скрыть от редактора, но использовать в коде. Сделать два свойства с одинаковым именем, разумеется, не получится, но здесь поможет атрибут DisplayName. Задание атрибута DesignerSeriaiizationvisibiiity (см. разд. 10.4) позволяет не сохранять одно и то же значение два раза. Код получившегося компонента показан в листинге 8.10. Проблема с двойным щелчком исправлена.

Листинг 8 10. "Тоневое" сиоисгио для исправлении проблемы с двойным щелчком

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")] public partial class GradientLabel : Label (

GradientParameters gradient =

new GradientParameters(Color.LightGreen, Color.DarkBlue); public GradientLabel()

{

InitializeComponent();

}

// Это свойство видно редактору свойств [Category("Gradient")]

[Description("Свойства градиентной заливки")] [TypeConverter(typeof(GradientConverter))] [DisplayName("Gradient")] [DesignerSerializationVisibility(

DesignerSerializationVisibility.Hidden)]

public object GradientProp

{

get ( return Gradient; ) set

{

// Если это реальное значение,

// а не ошибочный string — сохраняем

if (value.GetType() — typeof(GradientParameters))

{

Gradient = value as GradientParameters;

}

}

}

// Это свойство не видно редактору свойств, // но его удобно использовать в коде [Browsable(false) ] public GradientParameters Gradient

{

get { return gradient; }

set {

gradient value; GradientPropertyChanged{);

}

}

protected override void OnPaint(PaintEventArgs pe) {

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

Color cl = Color.FromArgbdOO, Gradient. StartColor) ; Color c2 = Color.FromArgb(100, Gradient.EndColor); Brush b =• new Drawing2D.LinearGradientBrush(

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

}

// Обработчик изменения свойств градиентной заливки

void GradientPropertyChanged()

{

// Перерисовать компонент Invalidate();

}

}

Литература:

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

По теме:

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