Редактирование значения свойства как текста затруднено, если свойство имеет нестандартный тип, предполагающий множество значений. Таковым, например, является тип TFont или вообще любой другой тип-класс. Для редактирования подобного свойства обычно создается специальное диалоговое окно, имеющее переменную того же типа, что и свойство. В этом случае задачи собственно редактирования (изменения) значения свойства решаются в этом окне, а роль редактора сводится к динамическому созданию окна, переносу в его переменную текущего значения свойства и контролю успешности редактирования: если редактирование успешно, редактор переносит содержимое переменной в значение редактируемого свойства. Замечательной особенностью этого подхода является то, что редактируемое значение вовсе не обязано преобразовываться в текст и обратно. Дело в том, что за отображение текущего значения в окне Инспектора объектов ответственен метод GetValue, который может,'вообще говоря, не возвращать ничего. (На практике он обычно возвращает строку с именем класса свойства.) Другим случаем применения диалогового окна является стремление упростить пользователю ввод. Если, например, в строковом свойстве хранится громоздкий маршрут доступа к файлу, пользователю будет значительно удобнее указать этот маршрут с помощью диалогового окна компонента OpenDialog.

Чтобы проиллюстрировать применение диалоговых окон для редактирования свойств, рассмотрим два примера. Оба они связаны с перекрытием метода Edit класса TPropertyEditor.

В 14.4 рассматривается компонент FarRunButton, который запускает некоторую программу в заданный момент времени и контролирует ее завершение, возбуждая событие Af terExecute. Его опубликованное свойство AppName определяет имя исполняемого файла. Это свойство описано так:

type TAppName = type String; TFarRunButton = class(TSpeedButton) private FAppName: TAppname; puplished

property AppName: TAppName read FAppName write FAppName;
end;

Несмотря на то, что тип TAppName описан специальным образом (о причинах этого мы поговорим в 11.1.5), по сути он ничем не отличается от обычного строкового типа и легко обслуживается стандартным редактором TStringProperty. Причина создания для этого свойства специального редактора связана не с трудностями преобразования значения, а с желанием облегчить пользование компонентом.

В листинге 11.1 представлен модуль редактора.

Листинг 11.1. Модуль редактора свойства AppName unit AppNameProperty;

Этот модуль содержит редактор свойства AppName компонента FarRunButton. При щелчке на кнопке в строке значения свойства Инспектор объектов вызывает этот редактор, который создает и показывает диалоговое окно выбора исполняемых файлов, =========================================^

interface

uses Designlntf, DesignEditors; // He пытайтесь отлаживать

// этот модуль до его регистрации // в среде Delphi!

type TAppNameProperty = class(TPropertyEditor) procedure Edit;
override;
function GetAttributes: TPropertyAttributes;
override; function GetValue: String;
override; procedure SetValue(const Value: String);
override;
end;

procedure Register; implementation

uses FarRunButton, Dialogs, SysUtils;
procedure Register;
begin
RegisterPropertyEditor(Typelnfо(TAppName), NIL, '', TAppNameProperty);
end;

/ TAppNamePropEd } procedure TAppNameProperty.Edit; var Dig: TOpenDialog; begin Dig := TOpenDialog.Create(Nil); // Создаем диалог try

with Dig do
begin
// Определяем его параметры FileName:= Value;
DefaultExt := 'EXE';

продолжение

Листинг 11.1 (продолжение) Filter := 'Исполняемые файлы|*.EXE|Любые файлы!*.*1; // Выполняем диалог if Execute then Value := FileName; end; finally

FreeAndNil(Dig);
end end;
function TAppNameProperty.GetAttributes: TPropertyAttributes;
begin
Result := [paDialog] end;
function TAppNameProperty .GetValue: String;
f, begin
Result := GetStrValue end;
procedure TAppNameProperty.SetValue(const Value: String);
begin
SetStrValue(Value) end;

end.

ПРИМЕЧАНИЕ -

По соглашению имена классов редакторов свойств оканчиваются словом «Property», например: TCursorProperty, TFontProperty, TAppNameProperty.

В этом модуле нет ничего необычного за исключением перекрытия метода GetAttributes. Подробнее об этом методе мы поговорим в 11.1.4, а сейчас я лишь сообщу, что наличие среди атрибутов значения paDialog предписывает Инспектору объектов вставить в строку свойства кнопку вызова диалогового окна редактора. Заметьте, что метод Edit редактора вызывается всякий раз после щелчка на этой кнопке.

ПРИМЕЧАНИЕ -

Еще раз хочу обратить ваше внимание на невозможность автономной отладки модуля: компилятор просто не найдет нужных файлов. Более того, почти все они имеются в исходных текстах в папке $(DELPHI)\Source\ToolseAPI. Не хватает малого - модуля Proxies. В первых версиях Delphi (до версии 4 включительно) этот модуль поставлялся в виде DCU-файла. В последних версиях он входит в состав пакета deslgnide.dcp и автоматически включается редактором пакета в секцию Requires, если в пакет помещается модуль редактора свойства.

Другой пример более актуален. Речь пойдет о действительно нестандартном типе свойства (потомок ТЫэ^, значение которого представляет собой множество вещественных чисел.

В 14.3 описан компонент FarRgnButton, создающий кнопку (декоративную панель) произвольной неквадратной формы. Я не буду говорить о деталях реализации компонента, чтобы не отвлекаться от основной темы этой главы, скажу лишь, что одно из центральных свойств компонента - свойство Polygon, которое описывает полигон - замкнутый многоугольник. Вершины многоугольника (узлы полигона) определяются парами вещественных чисел в диапазоне от 0 до 1, которые представляют собой относительные координаты вершин. Эти координаты преобразуются в абсолютные (пиксельные) путем их умножения на текущие размеры компонента. Требуется разработать редактор этого свойства, чтобы пользователь мог на этапе конструирования создавать самые разные полигоны и таким образом предельно разнообразить внешний вид кнопок в своем проекте.

Чтобы список TList мог легко манипулировать множеством пар вещественных чисел, вводится промежуточный тип TPolyPoint, перекрываются некоторые ключевые методы родительского класса и вводятся новые:

type PPolyPoint = ATPolyPoint; // Указатель на класс TPolyPoint TPolyPoint = record

X, Y: Real; // Запись TPolyPoint содержит // два вещественных числа

end;

// Тип ТPolygon - обычный список, приспособленный для работы // с парами вещественных чисел TPolygon = class(TList) public

function Add(X, Y: Real): Integer;
function GetX(Index): Real;
function GetY(Index): Real;
procedure Delete(Index: Integer);
procedure Cleare;
override;
end;
// Отрывок из описания класса TFarRgnButton TFarRgnButton = class(TGraphicComponent) private FPolygon: TPolygon;

public

constructor Create(AOwner: TComponent);
override;
destructor Destroy;
override;

published

property Polygon: TPolygon read FPolygon write FPolygon;
end;
Вот реализация методов: var
Р: PPolyPoint;
function TPolygon.Add(X, Y: Real): Integer; // Заносит в список очередную пару чисел begin
New(P);
РЛ.Х := X;
PA.Y := Y;
inherited Add(P) end;
function TPolygon.GetX(Index: Integer): Real; // Возвращает горизонтальную координату узла begin
Р :^ Items(Index);
Result := РЛ.Х end;
function TPolygon.GetY(Index: Integer): Real; // Возвращает вертикальную координату узла begin
Р := Items(Index);
Result := PA.Y end;
procedure TPolygon.Delete(Index: Integer); // Освобождает память от двух чисел begin
Р := Items(Index);
Dispose CP) end;
procedure TPolygon.Clear; // Очищает весь список var
k: Integer;
begin
for k := 0 to Count - 1 do
Delete(k);
inherited end;
constructor TFarRgnButton.Create(AOwner);
begin
inherited;
FPolygon := TPolygon.Create;
end;
destuctor TFarRgnButton.Destroy;
begin
FPolygon.Free;
inherited end;

Итак, будем считать, что мы закончили разработку модуля компонента (на самом деле - это довольно громоздкий компонент, см. 14.3), во всяком случае, в части, непосредственно касающейся редактора свойства Polygon. Теперь займемся собственно редактором.

Главная отличительная особенность редактора - невозможность символьного представления текущего значения свойства. В связи с этим выберем в качестве родительского класс TProprtyEditor и перекроем три его метода - Edit, GetAttributes и GetValue.

ПРИМЕЧАНИЕ -

Так как у значения свойства нет символьного эквивалента, можно было бы и не перекрывать метод GetValue, который ответственен за представление значения свойства в окне Инспектора объектов: в родительском классе этот метод возвращает пустую строку. По соглашению этот метод все-таки перекрывается, чтобы в строке значения свойства показать имя типа свойства.

В листинге 11.2 показан модуль редактора.

Листинг 11.2. Модуль редактора свойства Polygon unit PolyProperty;

Этот модуль содержит редактор свойства Polygon компонента FarRgnButton. Свойство Polygon хранит значение в виде множества вещественных чисел и не имеет строкового представления. Редактор вставляет в строку свойства кнопку для обращения к своему методу Edit, в котором создает и показывает диалоговое окно редактирования значения.

interface

uses Designlntf, DesignEditors; // He пытайтесь отлаживать этот

// модуль до его регистрации // в среде Delphi!

type TPolyProperty = class(TPropertyEditor) procedure Edit;
override;
function GetAttributes: TPropertyAttributes;
override; function GetValue: String;
override;
end;
procedure Register;

implementation

uses PolyComp, PolygonDlg, Controls;
продолжение &
Листинг 11.2 (продолжение) procedure Register;
begin
RegisterPropertyEditor(Typelnfо(TPolygon), TFarRgnButton, 'Polygon1, TPolyProperty);
end;
procedure TPolyProperty.Edit;

// Осуществляет взаимодействие свойства

// с диалоговым окном редактирования

begin // Создаем диалоговое окно:

PolyEditDlg TPolyEditDlg.Create(NIL) ;
try
{ В его глобальную переменную Poly, имеющую тип TPolygon, переносим 32-разрядный дескриптор аналогичного свойства компонента. Теперь окно получит текущий полигон компонента. Дескриптор свойства получаем методом GetOrdValue и преобразуем к нужному типу: }
Poly := TPolygon(GetOrdValue);

if PolyEditDlg.ShowModal - mrOk then // Диалог успешный? SetOrdValue(Integer(Poly)); // -Да. Переносим

// отредактированный полигон в компонент. Приводим к целому // типу для синтаксической корректности обращения к методу. finally

PolyEditDlg.Free // Разрушаем ненужное диалоговое окно end end;
function TPolyProperty.GetAttributes;
begin
Result := IpaDialog] end;
function TPolyProperty.GetValue: String;
begin
Result := 'TPolygon' end;

end.

ПРИМЕЧАНИЕ -:-

По соглашению имена классов нестандартных диалоговых окон оканчиваются словом «Ес1Ш1д», например: ТГ^еЬоокЕсШд, ТБ1гЕс1№01д, ТРо1уЕс1Шд.

Как видите, редактор TPolyProperty ничуть не сложнее рассмотренного ранее редактора TAppNameProperty, хотя свойство Polygon, в отличие от AppName, не имеет символьного представления. Его особенностью является непосредственное обращение к свойству Polygon компонента. Для этого в диалоговом окне редактора свойства предусмотрена глобальная переменная Poly типа TPolygon.

Перед выводом диалогового окна в нее копируется полигон, а при успешном закрытии диалогового окна новый полигон содержится в этой переменной. А что же со свойством Polygon компонента? Это свойство в нашем случае доступно как 32-разрядное значение редактируемого свойства, которое возвращается методом GetOrdValue.

ПРИМЕЧАНИЕ -

Если редактируемое свойство - объект, значение этого свойства хранит указатель, возвращаемый методом GetOrdValue.

11.1.2. Редактирование свойства как текста || Оглавление || 11.1.4. Определение атрибутов редакторов свойств


Искусство создания компонентов Delphi



Новости за месяц

  • Декабрь
    2019
  • Пн
  • Вт
  • Ср
  • Чт
  • Пт
  • Сб
  • Вс