Статьи Королевства Дельфи

       

Основные положения


Далее по тексту "особенностью" (Particularity) я буду называть свойство, событие или метод, заменяя тем самым словосочетание одним словом. Особенность - краеугольный камень реализации Инспектора. Физически особенность представляет собой запись TParticul: TParticulKind = (pkProperty, pkMethod, pkEvent); TParticul = record Name: string; Kind: TParticulKind; Data: Word; Enabled: Boolean; Visible: Boolean; Code: string; Info: string; ReadMode: Boolean; end; где

  • Name - имя особенности, отображаемое в Инспекторе, можно (и желательно!) на русском, каждая особенность обладает уникальным именем;
  • Kind - тип особенности, т. е. свойство это, метод или событие;
  • Data - шифр типа данных, служит для назначения данному событию определённого редактора (см. далее);
  • Enabled - показывает, разрешена особенность или запрещена;
  • Visible - показывает, видима особенность или нет (в основном для внутреннего использования, но можно использовать и в явном виде);
  • Code - кодированные данные в виде строки;
  • Info - дополнительные кодированные данные (например, для целых чисел - диапазон), не редактируются Инспектором;
  • ReadMode - особенность только для чтения (не работает в случае, когда особенность является методом).
В дальнейшем понадобится понятие массива данных TParticulList = class(TList). Этот класс - простой контейнер особенностей; при добавлении в него особенности он сразу же сортирует весь массив по именам особенностей. Также при добавлении особенности метод TParticulList.Add проверяет имя особенности (TParticul.Name) на уникальность; если особенность с таким именем уже содержится в массиве, создаётся исключительная ситуация EParticul.

Инспектор обрабатывает элементы управления специального вида, которые умеют генерировать массивы особенностей и принимать особенности:

TParticulControl = class(TCustomControl) private FCaption: string; protected function GetTypeName: string; virtual; abstract; function GetParticuls: TParticulList; virtual; abstract; procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override; public property Caption: string read FCaption write FCaption; property TypeName: string read GetTypeName; property Particuls: TParticulList read GetParticuls; function FullText: string; procedure SetParticul(Value: TParticul); virtual; abstract; end; где

  • Caption - имя элемента управления, отображаемое в Инспекторе (аналог свойства Name: TComponentName в Инспекторе Delphi);
  • GetTypeName - функция, выдающая название типа элемента управления (можно на русском!), также отображаемое в Инспекторе;
  • GetParticuls - функция, формирующая список особенностей данного элемента управления для передачи его в Инспектор;
  • MouseDown - обработчик щелчка мышью на элементе (далее будет рассмотрен подробнее);
  • FullText - формирует строку для отображения списка редактируемых объектов в Инспекторе (Result := FCaption + ': ' + GetTypeName);
  • SetParticul - осуществляет приём изменённой особенности из Инспектора.
Элементы TParticulControl можно использовать двумя способами. Первый - прямое использование; создаётся наследник, перекрывается, например, его метод Paint и элемент можно использовать. Этот способ подходит, например, в САПРах, где вся работа заключается только в редактировании элементов. Второй - косвенное использование; при этом способе TParticulControl служит как бы оболочкой для какого-либо другого элемента (не являющегося наследником TParticulControl и, вообще говоря, даже не являющегося наследником TControl). Для второго способа существует более удобный класс: TExternalControl = class(TParticulControl) private FExternalObject: TObject; procedure SetExternalObject(Value: TObject); procedure WMEraseBkgnd(var Message: TWmEraseBkgnd); message WM_ERASEBKGND; protected procedure CreateParams(var Params: TCreateParams); override; procedure Paint; override; public property ExternalObject: TObject read FExternalObject write SetExternalObject; procedure Refresh; end; где
  • ExternalObject - указатель на внешний объект, оболочкой которому служит данный элемент управления;
  • WMEraseBkgnd и CreateParams - перекрыты для обеспечения прозрачности;
  • Refresh - обеспечивает перерисовку при изменении размеров оболочки.
Элемент TExternalControl построен таким образом, что если редактируемый объект является наследником TControl, то при редактировании отображается именно он (в силу прозрачности TExternalControl), а если не является - отображается симпатичный квадратик (подобно как в Delphi отображаются невизуальные компоненты).


Элементы управления TParticulControl обрабатываются только одним способом - щелчок мышью (с нажатой клавишей Shift или без неё). При щелчке без нажатия Shift элемент добавляется в список активных элементов (т. е. тех, которые обрабатываются в настоящий момент Инспектором) Actives: TList, который предварительно очищается. При щелчке при нажатой Shift элемент также добавляется в Actives, но без предварительной его очистки.

Для того чтобы особенности отображались в Инспекторе, они должны быть предварительно зарегистрированы процедурой: RegisterData(Data: Word; AEditor: TParticulEditorClass; AExecutor: TExecutor); где
  • Data - уникальный номер для регистрируемого типа;
  • AEditor - ссылка (указатель на класс) на класс редактора (см. ниже);
  • AExecutor - обрабатывающая процедура (см. ниже).
Если будет сделана попытка зарегистрировать особенности под уже имеющимся номером, возникнет исключительная ситуация ERegister.
К каждой особенности, благодаря регистрации, привязывается редактор определённого класса и процедура обработки следующего типа: TExecutor = function(Code, Info: string; var Changed: Boolean; ReadMode: Boolean = False): string; где
  • Code - кодированные данные из TParticul.Code;
  • Info - дополнительные кодированные данные из TParticul.Info;
  • Changed - булева переменная, показывающая, были ли сделаны изменения (True) или нет (False);
  • ReadMode - запрещение изменения Code (по умолчанию - False).


Применение этой процедуры будет показано ниже.

Редактор особенностей представляет собой наследника от класса TParticulEditor, описание которого дано ниже: TParticulEditor = class protected FOldCode: string; FParticul: TParticul; FExecutor: TExecutor; procedure Init(AControl: TWinControlClass); procedure SetParticul(Value: TParticul); virtual; public Control: TWinControl; property Executor: TExecutor read FExecutor write FExecutor; property Particul: TParticul read FParticul write SetParticul; constructor Create; virtual; destructor Destroy; override; procedure Make; end; TParticulEditorClass = class of TParticulEditor; где
  • Init - процедура, создающая редактор класса AControl, строго обязательна в конструкторе;
  • Control - то, что будет отображено в Инспекторе (собственно редактор);
  • Partucul - редактируемое свойство;
  • Executor - процедура-обработчик типа TExecutor;
  • Make - обновление Инспектора.
Немного остановлюсь на методе SetParticul, который изменяет внешний вид Control при различных значениях полей TParticul (Code, Info, Enabled, ReadMode). Так, например, во всех редакторах Enabled присваивается элементу управления (Control.Enabled := Value.Enabled); ReadMode в TEditEditor'е присваивается TEdit'у ((Control as TEdit).ReadMode := Value.ReadMode); а через Info в ComboBox передаются все элементы, из которых необходимо сделать выбор.


Содержание раздела