Sign in Join
Wiki copytemporary

How To Write Lazarus Component/ru: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: How To Write Lazarus Component/ru

OldNewLine
1+Это руководство по созданию компонентов.
2+
3+## Шаг 1: Создание пакета
4+
5+- В меню IDE Lazarus нажмите "Package" \> "New package" для запуска диспетчера пакетов.
6+
7+<figure>
8+<img src="package_menu.png" title="package_menu.png" />
9+<figcaption>package_menu.png</figcaption>
10+</figure>
11+
12+- Появится диалоговое окно "Save". Выберите папку и имя файла и нажмите "Save". Если программа IDE предложит использовать строчные имена файлов, нажмите "да".
13+
14+- Поздравляем: вы только что создали свой первый пакет!
15+
16+<figure>
17+<img src="How_to_write_lazarus_component_package_maker.png" title="Package Maker" />
18+<figcaption>Package Maker</figcaption>
19+</figure>
20+
21+## Шаг 2: Создание модуля
22+
23+Вы можете создать новый модуль или использовать существующий файл. Оба варианта описаны ниже.
24+
25+### Создание нового модуля
26+
27+- Используйте кнопку **Add \> New component**.\
28+
29+![](/wiki-images/5/56/Package_new_component.png)\
30+
31+- Выберите компонент, \[например такой\] как TComboBox.
32+- Выберите *customcontrol1.pas* в качестве **Unit File Name** и *CustomControl1* в качестве **Unit Name**.
33+- Теперь вы можете задать иконку компоненту и определить, на какой палитре компонент должен появиться позже в Lazarus-IDE.
34+- Нажмите **OK**\[, чтобы создать новый компонент\].\
35+
36+``` pascal
37+unit CustomControl1;
38+
39+{$mode objfpc}{$H+}
40+
41+interface
42+
43+uses
44+ Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls;
45+
46+type
47+ TCustomControl1 = class(TComboBox)
48+ private
49+ { Private declarations }
50+ protected
51+ { Protected declarations }
52+ public
53+ { Public declarations }
54+ published
55+ { Published declarations }
56+ end;
57+
58+procedure Register;
59+
60+implementation
61+
62+procedure Register;
63+begin
64+ RegisterComponents('Standard', [TCustomControl1]);
65+end;
66+
67+end.
68+```
69+
70+- Установите пакет, кликнув кнопку **Use -\> Install** вверху редактора пакетов.\
71+
72+![](/wiki-images/3/36/Package_install.png)\
73+
74+- Теперь IDE спросит вас, должна ли она быть пересобрана. Ответьте "Да".\
75+
76+![](/wiki-images/9/9b/Package_rebuild.png)\
77+
78+- Перезапустите Lazarus\[, если он не сделал этого сам,\] и посмотрите на свой новый компонент в палитре компонентов. Поздравляем: вы только что установили свой первый пакет с вашим первым компонентом.\
79+
80+![](/wiki-images/3/35/Package_installed.png)\
81+
82+> **Note:** Если вы не видите свой новый компонент в палитре компонентов, скорее всего, вы используете не пересобранную версию Lazarus. Вы можете установить, где Lazarus пересобирается, используя \[пункт меню\] Tools -\> Options -\> Files -\> Lazarus directory.
83+>
84+> Вместо прямого вызова Lazarus вы также можете использовать startlazarus, который запускает недавно созданный Lazarus, например исполняемый файл Lazarus в каталоге ~ / .lazarus, если у вас нет доступа на запись в каталог, в который был установлен Lazarus.
85+
86+------------------------------------------------------------------------
87+
88+Прим.перев. 23:36, 1 December 2018 (CET): В корневом каталоге Lazarus'а после пересборки обычно содержится три исполняемых файла: lazarus (текущий запускаемый файл), startlazarus (лаучер для lazarus) и lazarus.old (старая версия исполняемого файла, существующая после последней удачной пересборки среды). В примечании, вероятнее всего, речь идет о них.
89+
90+------------------------------------------------------------------------
91+
92+### Добавление существующего модуля
93+
94+Если у вас уже есть модуль, вы можете добавить его в пакет:
95+
96+![](/wiki-images/1/11/Package_existing_unit.png)\
97+
98+- Нажмите кнопку **Add**, перейдите к вкладке **Add Files**. В \[колонке\] file name модуля добавьте ваш существующий файл. Нажмите **Add files to package**. Если диспетчер пакетов жалуется, что модуль находится вне заданных к модулям путей, нажмите "Да", чтобы добавить каталог в пути к модулям.
99+- Нажмите кнопку **Add** снова, перейдите на вкладку **Add Files**, найдите файл *.lrs* и нажмите "OK" (см. Шаг 3 о создании файла иконки).
100+- Снова нажмите кнопку **Add**, перейдите к вкладке **New Requirement** \[(новая зависимость)\]. В имени пакета выберите **LCL** и нажмите OK.
101+
102+Конечный результат должен выглядеть следующим образом:\
103+![Package Maker](/wiki-images/d/d9/How_to_write_lazarus_component_package_maker_done.png)\
104+
105+- Щелкните по дереву файлов в диспетчере пакетов. В свойствах файла убедитесь, что установлен флажок *Register unit*.
106+- Нажмите кнопку Options. Перейдите на вкладку IDE Integration. В опции Package Type убедитесь, что выбраны \[режимы\] Designtime и Runtime.
107+- Нажмите кнопку Compile, чтобы проверить, файлы компилируются без ошибок.
108+- Нажмите кнопку Install. Lazarus автоматически пересоберется и перезагрузится.
109+
110+Компонент создан и готов к использованию:\
111+![Component Created](/wiki-images/3/3c/How_to_write_lazarus_component_created.png)\
112+
113+## Шаг 3: Создание значков для пакета
114+
115+Вы должны создать PNG-файлы размером 24x24 пикселя в виде значков. Если для Lazarus 1.8+ вы хотите, чтобы значки палитры масштабировались при более высоких разрешениях экрана, вы также должны предоставлять значки 36x36 и 48x48 пикселей. Имена более крупных значков требуют добавления \[суффиксов\] "_150" и "_200" к имени файла, соответственно \[увеличению разрешения\] ("_150" для 150%, "_200" для 200%).
116+
117+FPC способен использовать стандартные файлы ресурсов `.rc` или скомпилированные ресурсы `.res`, \[начиная\] с версии fpc 2.6. См. [[Lazarus Resources/ru|FPC ресурсы]]. \[О файлах\] `.lrs`: см.ниже.
118+
119+### Использование редактора изображений Lazarus
120+
121+Вы можете использовать [[Lazarus Image Editor]] для создания изображений в .lrs формате.
122+
123+------------------------------------------------------------------------
124+
125+Прим.перев. 17:13, 3 December 2018 (CET): Поскольку старшие версии Лазаруса (насколько я помню, после v.1.6.х точно) позволяют задавать иконку изображения в png-формате, то удобнее всего для их создания использовать бесплатный векторный редактор [InkScape](https://inkscape.org/ru), "родным" для которого является формат SVG. Среди его преимуществ:
126+
127+- работа со слоями
128+- поддержка альфа-канала
129+- предосмотр иконок в разных разрешениях (в файле настроек можно задать даже нестандартные значения)
130+- вменяемый и оперативный саппорт (можно напрямую пообщаться с разработчиками в [чате](https://inkscape.org/ru/community/discussion))
131+- экспорт изображений в png-формат
132+
133+------------------------------------------------------------------------
134+
135+### Использование lazres
136+
137+`lazres` обычно находится в каталоге инструментов Lazarus.
138+
139+Вам может понадобиться скомпилировать [[lazres]] при первом использовании. Просто откройте *lazres.lpi* в среде IDE и нажмите **run \> build** в меню.
140+
141+#### Создание lrs-файла
142+
143+Для создания lrs-файла запустите:
144+
145+``` pascal
146+~/lazarus/tools/lazres samplepackage.lrs TMyCom.png
147+```
148+
149+или, если доступны изображения с высоким разрешением
150+
151+``` pascal
152+~/lazarus/tools/lazres samplepackage.lrs TMyCom.png TMyCom_150.pgn TMyCom_200.png
153+```
154+
155+где
156+
157+- *samplepackage* - имя вашего пакета
158+- *TMyCom* - имя вашего компонента. Имя изображения должно совпадать с именем вашего компонента!
159+
160+Вы можете добавить несколько изображений в файл lrs, добавив имя файла изображения в конец \[команды\]. Например:
161+
162+``` pascal
163+~/lazarus/tools/lazres samplepackage.lrs TMyCom.png TMyOtherCom.png ...
164+```
165+
166+**Пример**
167+
168+Ниже приведен образец полученного файла *samplepackage.lrs*.
169+
170+``` pascal
171+LazarusResources.Add('TMyCom','PNG',[
172+ #137'PNG'#13#10#26#10#0#0#0#13'IHDR'#0#0#0#24#0#0#0#24#8#2#0#0#0'o'#21#170#175
173+ +#0#0#0#4'gAMA'#0#0#177#143#11#252'a'#5#0#0#0'|IDAT8O'#237#212#209#10#192' '#8
174+ +#5'P'#247#231#251's'#215#138#133#164#166'\'#220#195'`'#209'c'#157'L'#173#131
175+ +#153#169'd4'#168'dP'#137'r_'#235'5'#136'@Zmk'#16'd9'#144#176#232#164'1'#247
176+ +'I'#8#160'IL'#206'C'#179#144#12#199#140'.'#134#244#141'~'#168#247#209'S~;'#29
177+ +'V+'#196#201'^'#10#15#150'?'#255#18#227#206'NZ>42'#181#159#226#144#15'@'#201
178+ +#148#168'e'#224'7f<@4'#130'u_YD'#23#213#131#134'Q]'#158#188#135#0#0#0#0'IEND'
179+ +#174'B`'#130
180+]);
181+```
182+
183+Обязательно включите свой файл ресурсов в исходник нового компонента, добавив нижеследующее в модуль вашего компонента и включив "LResources" в раздел uses
184+
185+``` pascal
186+initialization
187+ {$I samplepackage.lrs}
188+```
189+
190+#### Создание res-файла
191+
192+lazres также может создать скомпилированный файл ресурсов, просто укажите файл с расширением .res, например:
193+
194+``` pascal
195+~/lazarus/tools/lazres samplepackage.res TMyCom.png
196+```
197+
198+или, для иконок с высоким разрешением:
199+
200+``` pascal
201+~/lazarus/tools/lazres samplepackage.res TMyCom.png TMyCom_150.png TMyCom_200.png
202+```
203+
204+В этом случае вместо включения файла lrs в разделе инициализации просто укажите файл ресурсов в любом месте исходного файла компонента
205+
206+``` pascal
207+ {$R samplepackage.res}
208+```
209+
210+### Использование glazres
211+
212+**GLazRes** это графическая версия `lazres`, которая может собирать файлы в файл ресурсов Lazarus (.lrs). Его можно найти в каталоге инструментов установки Lazarus.
213+
214+## Перекомпиляция пакетов
215+
216+Вам нужно пересобирать пакет каждый раз, когда вы вносите изменения в файл *mycom.pas*. Чтобы пересобрать пакет, откройте файл *samplepackage.lpk* в диспетчере пакетов и нажмите кнопку Install.
217+
218+## Удаление пакетов
219+
220+- Чтобы удалить установленные компоненты: в меню IDE выберите Package \> Configure installed packages. На следующем рисунке показан инструмент Installed Packages.
221+
222+![`Installed Components`](/wiki-images/0/0e/How_to_write_lazarus_component_installed_components.png)
223+
224+- Выберите пакет, который вы хотите удалить, и нажмите Uninstall selection.
225+
226+Если что-то пойдет не так с пакетом (например, каталог пакета удаляется без деинсталляции \[пакета в первую очередь\]), Lazarus может не разрешить вам удалять пакеты. Чтобы устранить проблему, в меню IDE выберите Tools \> Build Lazarus. Lazarus пересоберет все пакеты и перезапустит их. Теперь вы можете удалить проблемные пакеты.
227+
228+## Усовершенствование mycom.pas
229+
230+- Код в *mycom.pas* выше дает вам основы того, что вам нужно для создания компонента. Далее приведена усовершенствованная версия с некоторыми советами о том, как писать процедуры и события для компонентов.
231+- *OnChange2* показывает, как создавать события.
232+- *OnSample* показывает, как создавать пользовательские события.
233+- *MyText* и *MyText2* показывает различные способы записи свойств.
234+- Вы можете использовать TComboBox вместо TCustomComboBox в качестве базового класса, который \[объявляет\] публичными все свойства как TComboBox.
235+- Если TCustomComboBox используется в качестве базового класса, вы заметите, что в Инспекторе объектов IDE будет отсутствовать множество свойств и событий. Чтобы добавить эти свойства и события, просто скопируйте и вставьте свойства, перечисленные ниже \[комментария\] *// properties from TComboBox*. Этот список свойств можно получить из объявления TComboBox в модуле StdCtrls. Исключите \[из списка\] любое свойство, которое вы хотите \[определить\] самостоятельно.
236+
237+``` pascal
238+unit mycom;
239+
240+{$mode objfpc}{$H+}
241+
242+interface
243+
244+uses
245+ Classes, SysUtils, StdCtrls, Forms, Dialogs,
246+ LCLType,LCLIntf,lresources,LCLProc;
247+
248+type
249+
250+ TSampleEvent = procedure(MyText: String) of Object;
251+
252+ TMyCom = class (TCustomComboBox)
253+ private
254+ FMyText: String;
255+ FOnChange2: TNotifyEvent;
256+ FOnSample: TSampleEvent;
257+ public
258+ constructor Create(TheOwner: TComponent); override;
259+ procedure CreateWnd; override;
260+ procedure Change; override;
261+ protected
262+ function GetMyText2: String;
263+ procedure SetMyText2(MyText: String);
264+ published
265+ property MyText: String read FMyText write FMyText;
266+ property MyText2: String read GetMyText2 write SetMyText2;
267+ property OnChange2: TNotifyEvent read FOnChange2 write FOnChange2;
268+ property OnSample: TSampleEvent read FOnSample write FOnSample;
269+
270+ // properties from TComboBox
271+ property Align;
272+ property Anchors;
273+ property ArrowKeysTraverseList;
274+ property AutoComplete;
275+ property AutoCompleteText;
276+ property AutoDropDown;
277+ property AutoSelect;
278+ property AutoSize;
279+ property BidiMode;
280+ property BorderSpacing;
281+ property CharCase;
282+ property Color;
283+ property Ctl3D;
284+ property Constraints;
285+ property DragCursor;
286+ property DragMode;
287+ property DropDownCount;
288+ property Enabled;
289+ property Font;
290+ property ItemHeight;
291+ property ItemIndex;
292+ property Items;
293+ property ItemWidth;
294+ property MaxLength;
295+ property OnChange;
296+ property OnChangeBounds;
297+ property OnClick;
298+ property OnCloseUp;
299+ property OnContextPopup;
300+ property OnDblClick;
301+ property OnDragDrop;
302+ property OnDragOver;
303+ property OnDrawItem;
304+ property OnEndDrag;
305+ property OnDropDown;
306+ property OnEditingDone;
307+ property OnEnter;
308+ property OnExit;
309+ property OnGetItems;
310+ property OnKeyDown;
311+ property OnKeyPress;
312+ property OnKeyUp;
313+ property OnMeasureItem;
314+ property OnMouseDown;
315+ property OnMouseMove;
316+ property OnMouseUp;
317+ property OnStartDrag;
318+ property OnSelect;
319+ property OnUTF8KeyPress;
320+ property ParentBidiMode;
321+ property ParentColor;
322+ property ParentCtl3D;
323+ property ParentFont;
324+ property ParentShowHint;
325+ property PopupMenu;
326+ property ReadOnly;
327+ property ShowHint;
328+ property Sorted;
329+ property Style;
330+ property TabOrder;
331+ property TabStop;
332+ property Text;
333+ property Visible;
334+ end;
335+
336+procedure Register;
337+
338+implementation
339+
340+procedure Register;
341+begin
342+ RegisterComponents('Sample',[TMyCom]);
343+end;
344+
345+constructor TMyCom.Create(TheOwner: TComponent);
346+begin
347+ inherited Create(TheOwner);
348+ Self.Style := csDropDownList;
349+end;
350+
351+procedure TMyCom.CreateWnd;
352+begin
353+ inherited CreateWnd;
354+ Items.Assign(Screen.Fonts);
355+end;
356+
357+procedure TMyCom.Change;
358+begin
359+ inherited;
360+ if Assigned(FOnChange2) then FOnChange2(Self);
361+ if Assigned(FOnSample) then FOnSample(FMyText);
362+end;
363+
364+function TMyCom.GetMyText2: String;
365+begin
366+ Result:=FMyText;
367+end;
368+
369+procedure TMyCom.SetMyText2(MyText: String);
370+begin
371+ FMyText:=MyText;
372+end;
373+
374+initialization
375+ {$I samplepackage.lrs}
376+
377+end.
378+```
379+
380+Также вы заметите, что в Инспекторе объектов IDE существуют некоторые необъявленные и, возможно, нежелательные элементы. Чтобы удалить те из них, которые вам не нужны, вы можете переобъявить их в разделе *Published* как простые переменные. Например:
381+
382+``` pascal
383+Published
384+...
385+ property Height: Integer;
386+ property Width: Integer;
387+...
388+```
389+
390+## Использование встроенных (визуальных) компонентов
391+
392+Можно использовать стандартные компоненты, встроенные в ваши собственные компоненты (посмотрите для примера на TLabeledEdit или TButtonPanel).
393+
394+Предположим, вы хотите создать пользовательскую панель с TLabel на ней. С помощью описанных выше шагов можно создать базовый пакет и исходные файлы. Теперь сделайте следующее для добавления TLabel в компонент:
395+
396+- Добавьте приватный атрибут (поле) для компонента label (`FEmbeddedLabel: TLabel;`).
397+- Добавьте published-свойство только для чтения для компонента label (`property EmbeddedLabel: TLabel read FEmbeddedLabel;`)
398+- Создайте label в конструкторе (переопределенном) компонента (`FEmbeddedLabel: = TLabel.Create(self);`)
399+- Задайте родительский элемент компонента (`FEmbeddedLabel.Parent: = self;`)
400+- Если встроенный компонент не является 'субкомпонентом' по умолчанию (например, TBoundLabel, TPanelBitBtn и т.д.), то добавьте вызов в SetSubComponent. Это необходимо для IDE, чтобы она знала, что она также должна хранить свойства встроенного компонента. По умолчанию TLabel не является субкомпонентом, поэтому необходимо добавить вызов метода (`FEmbeddedLabel.SetSubComponent(true);`).
401+
402+В итоге вы получите что-то вроде этого (показаны только основные части):
403+
404+``` pascal
405+TEnhancedPanel = class(TCustomControl)
406+private
407+ { Новые аттрибуты для встроенного label }
408+ FEmbeddedLabel: TLabel;
409+
410+public
411+ { Конструктор должен быть переопределен, чтобы label могла быть создана}
412+ constructor Create(AOwner: TComponent); override;
413+
414+published
415+ { Сделаем label видимым в IDE }
416+ property EmbeddedLabel: TLabel read FEmbeddedLabel;
417+end;
418+
419+implementation
420+
421+constructor TEnhancedPanel.Create(AOwner: TComponent);
422+begin
423+ inherited Create(AOwner);
424+
425+ // Устанавливаем дефолтовые width и height
426+ with GetControlClassDefaultSize do
427+ SetInitialBounds(0, 0, CX, CY);
428+
429+ // Добавляем встроенный label
430+ FEmbeddedLabel := TLabel.Create(Self); // Добавляем встроенный label
431+ FEmbeddedLabel.Parent := self; // Показываем label на panel
432+ FEmbeddedLabel.SetSubComponent(true); // Говорим IDE сохранять измененные свойства
433+ FLabel.Name := 'EmbeddedLabel';
434+ FLabel.Caption := 'Howdy World!';
435+
436+ // Убеждаемся, что встроенная label не может быть выделена/удалена средствами IDE
437+ FLabel.ControlStyle := FLabel.ControlStyle - [csNoDesignSelectable];
438+
439+ // Задаем другие свойства, если необходимо
440+ //...
441+
442+end;
443+```
444+
445+## Использование пользовательской процедуры рисования
446+
447+Вы всегда можете переопределить компонент внутри вашей программы. Например, это реализует пользовательскую процедуру Paint для TLabel:
448+
449+``` pascal
450+type
451+ TMyLabel = class(TLabel)
452+ protected
453+ procedure Paint; override;
454+ end;
455+{...}
456+implementation
457+{...}
458+procedure TMyLabel.Paint;
459+begin
460+ // ваш код для реализации Paint, например
461+ Canvas.TextOut(0,0,Caption);
462+end;
463+```
464+
465+Теперь вы можете создать MyLabel внутри вашей программы во время выполнения с этой переопределенной процедурой Paint вместо стандартной.
466+
467+Для большинства компонентов и для большинства методов было бы полезно вызвать унаследованную процедуру внутри него:
468+
469+``` pascal
470+procedure TMyLabel.Paint;
471+begin
472+
473+ inherited Paint; /////////////////////
474+
475+ // ваш код для реализации Paint, например
476+ Canvas.TextOut(0,0,Caption);
477+end;
478+```
479+
480+Однако унаследованное поведение нежелательно в этом случае, поскольку второе действие записи будет перекрывать первый (унаследованный).
481+
482+## Интеграция компонента с IDE
483+
484+### Редакторы свойств
485+
486+Редакторы свойств предоставляют настраиваемые диалоги для редактирования свойств в инспекторе объектов. Для большинства свойств, таких как строки, строковые списки, изображения, перечисленные типы и другие, уже есть редакторы свойств по умолчанию, но если у пользовательского компонента есть особый вид свойства, для изменения свойства может потребоваться настраиваемый диалог.
487+
488+Каждый редактор свойств - это класс, который должен происходить из TPropertyEditor или одного из его потомков и реализовывать методы этого базового класса. Они должны быть зарегистрированы в процедуре 'Register', используя функцию RegisterPropertyEditor из модуля PropEdits. Это стандарт для имен редакторов свойств с именем свойства, за которым следует \[слово\] 'Property', например \[имя\] TFieldProperty для редактора свойств свойства TField.
489+
490+``` pascal
491+ TPropertyEditor = class
492+ public
493+ function AllEqual: Boolean; Virtual;
494+ function AutoFill: Boolean; Virtual;
495+ procedure Edit; Virtual; // Активируется двойным щелчком по значению свойства
496+ procedure ShowValue; Virtual; // Активируется щелчком по элементу управления значения свойства
497+ function GetAttributes: TPropertyAttributes; Virtual;
498+ function GetEditLimit: Integer; Virtual;
499+ function GetName: ShortString; Virtual;
500+ procedure GetProperties(Proc: TGetPropEditProc); Virtual;
501+ function GetHint(HintType: TPropEditHint; x, y: integer): String; Virtual;
502+ function GetDefaultValue: AnsiString; Virtual;
503+ procedure GetValues(Proc: TGetStrProc); Virtual;
504+ procedure SetValue(const NewValue: AnsiString); Virtual;
505+ procedure UpdateSubProperties; Virtual;
506+ function SubPropertiesNeedsUpdate: Boolean; Virtual;
507+ function IsDefaultValue: Boolean; Virtual;
508+ function IsNotDefaultValue: Boolean; Virtual;
509+ // ... укорочено
510+ end;
511+```
512+
513+Хорошим примером для редактора свойств является редактор свойств TFont.
514+
515+Один из наиболее распространенных случаев для редакторов свойств - это свойства, которые являются классами. Поскольку классы имеют много полей и могут иметь различные форматы, Lazarus не может иметь \[универсальное\] поле редактирования в инспекторе объектов, способное их редактировать, \[как это сделано,\] например, для строк и числовых типов.
516+
517+Для классов соглашение состоит в том, чтобы в поле значений постоянно отображалось имя класса в круглых скобках, например "(TFont)", а кнопка "..." показывала диалог для редактирования этого класса. Это поведение, за исключением диалога, реализуется стандартным редактором свойств для классов, называемых TClassPropertyEditor, который может быть унаследован при создании редакторов свойств для классов:
518+
519+``` pascal
520+TClassPropertyEditor = class(TPropertyEditor)
521+public
522+ constructor Create(Hook: TPropertyEditorHook; APropCount: Integer); Override;
523+ function GetAttributes: TPropertyAttributes; Override;
524+ procedure GetProperties(Proc: TGetPropEditProc); Override;
525+ function GetValue: AnsiString; Override;
526+ property SubPropsTypeFilter: TTypeKinds Read FSubPropsTypeFilter
527+ Write SetSubPropsTypeFilter
528+ Default tkAny;
529+end;
530+```
531+
532+Возвращаясь к примеру TFont, наследование от TClassPropertyEditor уже предлагает часть желаемого поведения, а затем классу TFontPropertyEditor необходимо реализовать отображение диалога в методе Edit и установить атрибуты для редактора:
533+
534+``` pascal
535+ TFontPropertyEditor = class(TClassPropertyEditor)
536+ public
537+ procedure Edit; Override;
538+ function GetAttributes: TPropertyAttributes; Override;
539+ end;
540+
541+procedure TFontPropertyEditor.Edit;
542+var
543+ FontDialog: TFontDialog;
544+begin
545+ FontDialog := TFontDialog.Create(NIL);
546+ try
547+ FontDialog.Font := TFont(GetObjectValue(TFont));
548+ FontDialog.Options := FontDialog.Options + [fdShowHelp, fdForceFontExist];
549+ if FontDialog.Execute then SetPtrValue(FontDialog.Font);
550+ finally
551+ FontDialog.Free;
552+ end;
553+end;
554+
555+function TFontPropertyEditor.GetAttributes: TPropertyAttributes;
556+begin
557+ Result := [paMultiSelect, paSubProperties, paDialog, paReadOnly];
558+end;
559+```
560+
561+### Редакторы компонентов
562+
563+Редакторы компонентов управляют поведением компонентов по щелчку правой кнопкой мыши и двойному щелчку в дизайнере форм.
564+
565+Каждый редактор компонентов - это класс, который должен происходить от TComponentEditor или одного из его потомков и реализовывать методы из этого базового класса. Он должен быть зарегистрирован в процедуре 'Register', использущей функцию RegisterComponentEditor из модуля ComponentEditors. Это стандарт для именования редакторов компонентов с именем компонента, за которым следует \[слово\] 'Editor', например TStringGridComponentEditor для редактора свойств компонента TStringGrid. Хотя редакторы пользовательских компонентов должны быть основаны на TComponentEditor, большинство его методов на самом деле \[берут начало\] от предка, поэтому необходимо также знать TBaseComponentEditor:
566+
567+``` pascal
568+ TBaseComponentEditor = class
569+ protected
570+ public
571+ constructor Create(AComponent: TComponent;
572+ ADesigner: TComponentEditorDesigner); Virtual;
573+ procedure Edit; Virtual; Abstract;
574+ procedure ExecuteVerb(Index: Integer); Virtual; Abstract;
575+ function GetVerb(Index: Integer): String; Virtual; Abstract;
576+ function GetVerbCount: Integer; Virtual; Abstract;
577+ procedure PrepareItem(Index: Integer; const AnItem: TMenuItem); Virtual; Abstract;
578+ procedure Copy; Virtual; Abstract;
579+ function IsInInlined: Boolean; Virtual; Abstract;
580+ function GetComponent: TComponent; Virtual; Abstract;
581+ function GetDesigner: TComponentEditorDesigner; Virtual; Abstract;
582+ function GetHook(out Hook: TPropertyEditorHook): Boolean; Virtual; Abstract;
583+ procedure Modified; Virtual; Abstract;
584+ end;
585+```
586+
587+Наиболее важным методом редактора компонентов является \[метод\] Edit, который вызывается по двойному щелчку на компоненте. Когда вызывается контекстное меню для компонента, запрашивается метод GetVerbCount и GetVerb для создания меню. Если выбран один из глаголов (что означает пункты меню в этом случае), вызывается ExecuteVerb. Существует редактор компонентов по умолчанию (TDefaultEditor), который реализует Edit для поиска свойств компонента для наиболее подходящего для редактирования. Обычно он выбирает событие, которое будет редактироваться, путем добавления скелета кода в редактор кода и установки курсора в это место.
588+
589+Другими важными методами из TBaseComponentEditor являются:
590+
591+- **ExecuteVerb(Index)** - Выполняет один из дополнительных пунктов меню, расположенных во всплывающем меню правой кнопки мыши;
592+- **GetVerb** – Возвращает имя каждого дополнительного пункта всплывающего меню. Обратите внимание, что редактор компонентов должен размещать специальные символы заголовка элемента, такие как &, для создания ускорителя клавиатуры и "-" для создания разделителя;
593+- **GetVerbCount** – Возвращает количество элементов, которые будут добавлены во всплывающее меню. Индекс для подпрограмм GetVerb и ExecuteVerb равен нулю, начиная от 0 до GetVerbCount - 1;
594+- **PrepareItem** – Вызывается для каждого глагола \[(GetVerbCount и GetVerb)\] после создания пункта меню. Позволяет настраивать элемент меню, например, добавляя подэлементы, добавляя флажок или даже скрывая его, устанавливая Visible в false;
595+- **Copy** - Вызывается, когда компонент копируется в буфер обмена. Данные компонента при использовании Lazarus'ом всегда будут \[только\] добавляться и не могут быть изменены. Этот метод предназначен для добавления различных типов из буфера обмена при вставке компонента в другие приложения, но не будет влиять на вставку в Lazarus'е.
596+
597+Простым и интересным примером является редактор компонентов TCheckListBox, который создает диалоговое окно для редактирования. Более удобный, чем реализация всех методов из TBaseComponentEditor, \[он\] наследуется от TComponentEditor, и это то, что делает TCheckListBoxEditor. Этот базовый класс добавляет пустые реализации для большинства методов и некоторые умолчательные для других. Для \[метода\] Edit он вызывает ExecuteVerb(0), поэтому, если первый пункт \[popup menu\] будет сопоставлен действию двойного щелчка мыши, что является условным обозначением для редактора, то нет необходимости в \[ручной\] реализации \[кода метода\] Edit. Это основное действие для двойного щелчка и первого элемента меню часто является диалогом, и для TCheckListBox это также реализуется \[так\]:
598+
599+``` pascal
600+ TCheckListBoxComponentEditor = class(TComponentEditor)
601+ protected
602+ procedure DoShowEditor;
603+ public
604+ procedure ExecuteVerb(Index: Integer); override;
605+ function GetVerb(Index: Integer): String; override;
606+ function GetVerbCount: Integer; override;
607+ end;
608+
609+procedure TCheckGroupComponentEditor.DoShowEditor;
610+var
611+ Dlg: TCheckGroupEditorDlg;
612+begin
613+ Dlg := TCheckGroupEditorDlg.Create(NIL);
614+ try
615+ // .. укорочено
616+ Dlg.ShowModal;
617+ // .. укорочено
618+ finally
619+ Dlg.Free;
620+ end;
621+end;
622+
623+procedure TCheckGroupComponentEditor.ExecuteVerb(Index: Integer);
624+begin
625+ case Index of
626+ 0: DoShowEditor;
627+ end;
628+end;
629+
630+function TCheckGroupComponentEditor.GetVerb(Index: Integer): String;
631+begin
632+ Result := 'CheckBox Editor...';
633+end;
634+
635+function TCheckGroupComponentEditor.GetVerbCount: Integer;
636+begin
637+ Result := 1;
638+end;
639+```
640+
641+## Отладка компонента в Design-time
642+
643+Чтобы отловить ошибки времени разработки во (вновь созданном) *dbgcomponent*:
644+
645+- откройте проект `C:\lazarus\ide\lazarus.lpi`;
646+- запустите проект;
647+- установите точку останова в *dbgcomponent* в главном (1-ом) приложении;
648+- используйте паскалевский код *dbgcomponent* во втором приложении;
649+- выполнить пошагово код компонента времени разработки в режиме отладки; делайте все, что необходимо;
650+
651+## См. также
652+
653+- [[Extending the IDE/ru|Extending the IDE]]
654+- [[Lazarus Packages/ru|Lazarus Packages]] - A guide for creating a package under Lazarus
655+- [[Components and Code examples/ru|Components and Code examples]]
656+- [[Lazarus Components]]
657+- [[IDE Window: Add to Package]]
658+- [[IDE Window: Create new package component]]
659+- [Создание визуальных компонент для Lazarus](http://freepascal.ru/article/lazarus/20050530152619) - статья Сергея Смирнова с русскоязычного ресурса
660+
661+Все вопросы вы можете оставлять также [на этой](http://forum.lazarus.freepascal.org/index.php/topic,5789.0.html) странице
662+
663+---
664+*From the Free Pascal wiki, page "How To Write Lazarus Component/ru", as it stood on 2020-02-17 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history