Sign in Join
Wiki copytemporary

How To Write Lazarus Component/zh CN: what changed

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

OldNewLine
11以下介绍如何创建控件。
22
33## 第1步: 创建一个包
44
55- 在Lazarus IDE菜单中选择 **Package \> New Package** 打开包管理器。
66
7-<figure>
8-<img src="package_menu.png" title="package_menu.png" />
9-<figcaption>package_menu.png</figcaption>
10-</figure>
7+![](/wiki-images/7/7c/Package_menu.png)
118
129- 设置好路径和文件名后点击**保存**,如果IDE提示使用小写文件名,点击点击允许。
1310
1411- 一个package就创建好了。
1512
16-<figure>
17-<img src="How_to_write_lazarus_component_package_maker.png" title="Package Maker" />
18-<figcaption>Package Maker</figcaption>
19-</figure>
13+![](/wiki-images/3/37/How_to_write_lazarus_component_package_maker.png)
2014
2115## 第2步: 创建一个单元
2216
2317你可以完全新建一个单元或者使用已有的单元,以下界面中包含了这两种选项。
2418
2519### 创建新的单元
2620
2721- 在包管理器中点击**Add button \> New component**。
2822
29-<figure>
30-<img src="package_new_component.png" title="package_new_component.png" />
31-<figcaption>package_new_component.png</figcaption>
32-</figure>
23+![](/wiki-images/5/56/Package_new_component.png)
3324
3425- 选择一个诸如TComboBox的控件。
3526- 选择*customcontrol1.pas*作为**单元文件名**,选择*CustomControl1*作为**单元名称**.
3627- 之后你可以给新的控件指定图标,以及决定它安装之后出现在哪个控件选项卡中。
3728- 点击**Create New Component**创建。
3829
3930``` pascal
4031unit CustomControl1;
4132
4233{$mode objfpc}{$H+}
4334
4435interface
4536
4637uses
4738 Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls;
4839
4940type
5041 TCustomControl1 = class(TComboBox)
5142 private
5243 { Private declarations }
5344 protected
5445 { Protected declarations }
5546 public
5647 { Public declarations }
5748 published
5849 { Published declarations }
5950 end;
6051
6152procedure Register;
6253
6354implementation
6455
6556procedure Register;
6657begin
6758 RegisterComponents('Standard', [TCustomControl1]);
6859end;
6960
7061end.
7162```
7263
7364- 现在你就可以加入自己的代码了。切记,尽量避免在控件尚未完善之前去安装控件,因为空间安装后就会成为IDE的一部分。如果这个空间存在bug,很有可能导致IDE崩溃甚至无法打开。所以安装测试请务必小心谨慎。
7465- 当控件测试好之后,便可以进行安装。点击"Package" \> "Install/uninstall packages",点击"Compile"进行编译,然后点击**Use -\> Install**进行安装:
7566
76-<figure>
77-<img src="package_install.png" title="package_install.png" />
78-<figcaption>package_install.png</figcaption>
79-</figure>
67+![](/wiki-images/3/36/Package_install.png)
8068
8169- 这时IDE就会询问是否重构,选择是。
8270
83-<figure>
84-<img src="package_rebuild.png" title="package_rebuild.png" />
85-<figcaption>package_rebuild.png</figcaption>
86-</figure>
71+![](/wiki-images/9/9b/Package_rebuild.png)
8772
8873- Lazarus会自动重新启动,重启后你就能看到新的控件在控件面板中,控件安装成功。
8974
90-<figure>
91-<img src="package_installed.png" title="package_installed.png" />
92-<figcaption>package_installed.png</figcaption>
93-</figure>
75+![](/wiki-images/3/35/Package_installed.png)
9476
9577> **Note:** If you do not see your new component in the component palette, it is most likely that you are not running the re-compiled version of Lazarus. You can set where Lazarus builds to using Tools -\> Options -\> Files -\> Lazarus directory.
9678>
9779> Instead of calling Lazarus directly, you also can use startlazarus, which starts the newly created Lazarus, for example the Lazarus executable in the ~/.lazarus directory, if you don't have write access to the directory Lazarus was installed into.
9880
9981> **Note:** 如果安装后出现错误,导致无法重新打开IDE,可以使用重构前自动备份的lazarus.old.exe替换新的lazarus.exe。
10082
10183### 使用已有的单元
10284
10385如果你已经有写好的单元,你可以将它加入这个包:
10486
105-<figure>
106-<img src="package_existing_unit.png" title="package_existing_unit.png" />
107-<figcaption>package_existing_unit.png</figcaption>
108-</figure>
87+![](/wiki-images/1/11/Package_existing_unit.png)
10988
11089- 点击**Add**按钮,选择**Add Files**选项卡。单元文件名一项浏览选择已有的单元文件,然后点击**Add files to package**。如果包管理器提示找不到单元文件不在单元路径中,按照提示允许添加此路径。
11190- 再次点击**Add**按钮,选择**Add Files**选项卡,浏览*.lrs*文件并点击确认(参考第3步)。
11291- 又一次点击**Add**按钮,选择**New Requirement**选项卡,在包名称一项选择**LCL**,然后点击确定。
11392
11493最后的效果应该是这样的:
11594
116-<figure>
117-<img src="How_to_write_lazarus_component_package_maker_done.png" title="Package Maker" />
118-<figcaption>Package Maker</figcaption>
119-</figure>
95+![](/wiki-images/d/d9/How_to_write_lazarus_component_package_maker_done.png)
12096
12197- Click under the Files tree in the Package Manager. In the File Properties, make sure *Register unit* is checked.
12298- Click the Options button. Select the IDE Integration tab. In Package Type, make sure Designtime and Runtime is selected.
12399- Click the Compile button to check to see that the files compile without errors.
124100- Click the Install button. Lazarus will rebuild and restart automatically.
125101
126102空间创建成功,效果如下:
127103
128-<figure>
129-<img src="How_to_write_lazarus_component_created.png" title="Component Created" />
130-<figcaption>Component Created</figcaption>
131-</figure>
104+![](/wiki-images/3/3c/How_to_write_lazarus_component_created.png)
132105
133106## 第3步: 创建图标
134107
135108你需要创建24x24像素大小的PNG图像作为图标,如果是 Laz 1.8以上版本,也可以额外提供36x36和48x48像素的图标,文件名之后加“_150”和“_200”用以表示150%和200%的缩放比例。
136109
137110FPC从2.6版本开始,可以使用标准资源文件格式(`.rc`)或已编译资源(`.res`)。参见[[Lazarus Resources|Lazarus_Resources#FPC_resources]]。
138111
139112至于使用`.lrs`文件:
140113
141114### 使用Laz图像编辑器
142115
143116你可以使用[[Lazarus Image Editor]]创建.lrs格式的图标。
144117
145118### 使用lazres
146119
147120`lazres`通常可以在Laz的工具目录中找到。
148121
149122第一次使用[[lazres]]时需要首先编译。 在IDE中打开*lazres.lpi*,然后点击菜单**run \> build**选项。
150123
151124**创建lrs文件**
152125
153126To create the lrs file run:
154127
155128`~/lazarus/tools/lazres samplepackage.lrs TMyCom.png`
156129
157130or, if high-resolution images are available
158131
159132`~/lazarus/tools/lazres samplepackage.lrs TMyCom.png TMyCom_150.pgn TMyCom_200.png`
160133
161134Where
162135
163136- *samplepackage* is the name of your package
164137- *TMyCom* is the name of your component! The name of the image must match the name of your component!
165138
166139You can add more than one image to the lrs file by appending the image file name at the end. Eg. ~/lazarus/tools/lazres samplepackage.lrs TMyCom.png TMyOtherCom.png ...
167140
168141**示例**
169142
170143Following is a sample of the resulting *samplepackage.lrs* file.
171144
172145 LazarusResources.Add('TMyCom','PNG',[
173146 #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
174147 +#0#0#0#4'gAMA'#0#0#177#143#11#252'a'#5#0#0#0'|IDAT8O'#237#212#209#10#192' '#8
175148 +#5'P'#247#231#251's'#215#138#133#164#166'\'#220#195'`'#209'c'#157'L'#173#131
176149 +#153#169'd4'#168'dP'#137'r_'#235'5'#136'@Zmk'#16'd9'#144#176#232#164'1'#247
177150 +'I'#8#160'IL'#206'C'#179#144#12#199#140'.'#134#244#141'~'#168#247#209'S~;'#29
178151 +'V+'#196#201'^'#10#15#150'?'#255#18#227#206'NZ>42'#181#159#226#144#15'@'#201
179152 +#148#168'e'#224'7f<@4'#130'u_YD'#23#213#131#134'Q]'#158#188#135#0#0#0#0'IEND'
180153 +#174'B`'#130
181154 ]);
182155
183156Make sure to include your resource file in your new component source by adding the following at the bottom your component's unit and including "LResources" in the uses clause
184157
185158``` pascal
186159initialization
187160 {$I samplepackage.lrs}
188161```
189162
190163**创建res文件**
191164
192165lazres can also create a compiled resource file, just specify a file with the .res extension, e.g.:
193166
194167`~/lazarus/tools/lazres samplepackage.`**`res`**` TMyCom.png`
195168
196169or, with high-res icons:
197170
198171`~/lazarus/tools/lazres samplepackage.`**`res`**` TMyCom.png TMyCom_150.png TMyCom_200.png`
199172
200173In this case, instead of including the lrs file in the initialization section, just include the resource file anywhere in your component source file
201174
202175``` pascal
203176 {$R samplepackage.res}
204177```
205178
206179### 使用glazres
207180
208181**GLazRes** is the graphical version of `lazres` that can assemble files into a Lazarus resource file (.lrs). It can be found in the tools directory of a Lazarus installation.
209182
210183## 重新编译包
211184
212185每一次修改*mycom.pas*文件后都需要重新编译包,需要在包管理器中打开*samplepackage.lpk*文件,而后点击Install按钮。
213186
214187## 移除包
215188
216189- 如需移除包,则需要在IDE菜单中点击**Package \> Install/Uninstall packages**选项,会跳出如下的界面:
217190
218191![`Installed Components`](/wiki-images/0/0e/How_to_write_lazarus_component_installed_components.png)
219192
220193- 选择你想要卸载的包。
221194
222195如果卸载过程出现问题(例如包的路径在未卸载前就已经被删除),Laz可能会不允许卸载某个包。解决方法是,点击IDE菜单**Tools \> Build Lazarus**,重新编译IDE并重启之后就可以卸载出问题的包了。
223196
224197## 加强mycom.pas
225198
226199- 上文中*mycom.pas*的代码只是创建了一个最基本的控件,接下来是一些进阶内容,包括如何编写过程和事件。
227200- *OnChange2*展示如何创建一个事件;
228201- *OnSample*展示如何创建一个自定义事件;
229202- *MyText*和*MyText2*展示了两种不同的属性定义方式;
230203- 你可以使用TComboBox而不是TCustomComboBox作为基类,这样可以避免发布TComboBox的所有的属性。
231204- 如果使用TCustomComboBox作为基类,你会发现IDE的Object Inspector中会少很多属性和事件。如果需要这些属性,需要复制*// properties from TComboBox*之后的内容。这些属性定义在StdCtrls单元的TComboBox类之中,如果你不希望某个属性出现在Object Inspector中,就在published区域忽略它。
232205
233206``` pascal
234207unit mycom;
235208
236209{$mode objfpc}{$H+}
237210
238211interface
239212
240213uses
241214 Classes, SysUtils, StdCtrls, Forms, Dialogs,
242215 LCLType,LCLIntf,lresources,LCLProc;
243216
244217type
245218
246219 TSampleEvent = procedure(MyText: String) of Object;
247220
248221 TMyCom = class (TCustomComboBox)
249222 private
250223 FMyText: String;
251224 FOnChange2: TNotifyEvent;
252225 FOnSample: TSampleEvent;
253226 public
254227 constructor Create(TheOwner: TComponent); override;
255228 procedure CreateWnd; override;
256229 procedure Change; override;
257230 protected
258231 function GetMyText2: String;
259232 procedure SetMyText2(MyText: String);
260233 published
261234 property MyText: String read FMyText write FMyText;
262235 property MyText2: String read GetMyText2 write SetMyText2;
263236 property OnChange2: TNotifyEvent read FOnChange2 write FOnChange2;
264237 property OnSample: TSampleEvent read FOnSample write FOnSample;
265238
266239 // properties from TComboBox
267240 property Align;
268241 property Anchors;
269242 property ArrowKeysTraverseList;
270243 property AutoComplete;
271244 property AutoCompleteText;
272245 property AutoDropDown;
273246 property AutoSelect;
274247 property AutoSize;
275248 property BidiMode;
276249 property BorderSpacing;
277250 property CharCase;
278251 property Color;
279252 property Ctl3D;
280253 property Constraints;
281254 property DragCursor;
282255 property DragMode;
283256 property DropDownCount;
284257 property Enabled;
285258 property Font;
286259 property ItemHeight;
287260 property ItemIndex;
288261 property Items;
289262 property ItemWidth;
290263 property MaxLength;
291264 property OnChange;
292265 property OnChangeBounds;
293266 property OnClick;
294267 property OnCloseUp;
295268 property OnContextPopup;
296269 property OnDblClick;
297270 property OnDragDrop;
298271 property OnDragOver;
299272 property OnDrawItem;
300273 property OnEndDrag;
301274 property OnDropDown;
302275 property OnEditingDone;
303276 property OnEnter;
304277 property OnExit;
305278 property OnGetItems;
306279 property OnKeyDown;
307280 property OnKeyPress;
308281 property OnKeyUp;
309282 property OnMeasureItem;
310283 property OnMouseDown;
311284 property OnMouseMove;
312285 property OnMouseUp;
313286 property OnStartDrag;
314287 property OnSelect;
315288 property OnUTF8KeyPress;
316289 property ParentBidiMode;
317290 property ParentColor;
318291 property ParentCtl3D;
319292 property ParentFont;
320293 property ParentShowHint;
321294 property PopupMenu;
322295 property ReadOnly;
323296 property ShowHint;
324297 property Sorted;
325298 property Style;
326299 property TabOrder;
327300 property TabStop;
328301 property Text;
329302 property Visible;
330303 end;
331304
332305procedure Register;
333306
334307implementation
335308
336309procedure Register;
337310begin
338311 RegisterComponents('Sample',[TMyCom]);
339312end;
340313
341314constructor TMyCom.Create(TheOwner: TComponent);
342315begin
343316 inherited Create(TheOwner);
344317 Self.Style := csDropDownList;
345318end;
346319
347320procedure TMyCom.CreateWnd;
348321begin
349322 inherited CreateWnd;
350323 Items.Assign(Screen.Fonts);
351324end;
352325
353326procedure TMyCom.Change;
354327begin
355328 inherited;
356329 if Assigned(FOnChange2) then FOnChange2(Self);
357330 if Assigned(FOnSample) then FOnSample(FMyText);
358331end;
359332
360333function TMyCom.GetMyText2: String;
361334begin
362335 Result:=FMyText;
363336end;
364337
365338procedure TMyCom.SetMyText2(MyText: String);
366339begin
367340 FMyText:=MyText;
368341end;
369342
370343initialization
371344 {$I samplepackage.lrs}
372345
373346end.
374347```
375348
376349同时你也会发现一些没有声明的,或是你不希望他出现在Object Inspector中的属性,如果需要移除这些属性,只需要在*Published*区域将它们定义成简单的变量即可,例如:
377350
378351``` pascal
379352Published
380353...
381354 property Height: Integer;
382355 property Width: Integer;
383356...
384357```
385358
386359## 使用嵌入式组件 embedded (visual) components
387360
388361可以将标准的空间嵌入你自己的控件,就像TLabeledEdit或TButtonPanel那样。
389362
390363Let's say you want to create a custom panel with a TLabel on it. With the steps described above the base package and source files can be created. 现在,按以下步骤就能够将TLabel控件嵌入自己的控件:
391364
392365- 添加一个私有的控件属性(FEmbeddedLabel: TLabel;);
393366- 为这个私有控件添加一个发布的只读属性(property EmbeddedLabel: TLabel read FEmbeddedLabel;);
394367- 在constructor的实现中创建这个label控件(FEmbeddedLabel := TLabel.Create(self););
395368- 将其parent属性修改为此控件(FEmbeddedLabel.Parent := self;);
396369- If the component to be embedded is not a 'subcomponent' by default (like TBoundLabel, TPanelBitBtn etc) then add the call to SetSubComponent. This is necessary for the IDE so it knows that it has to store the properties of the embedded component as well. TLabel is not a subcomponent by default so the call to the method must be added (FEmbeddedLabel.SetSubComponent(true);).
397370
398371总体来说,你需要像这样来定义(只展示了关键部分):
399372
400373``` pascal
401374TEnhancedPanel = class(TCustomControl)
402375private
403376 { The new attribute for the embedded label }
404377 FEmbeddedLabel: TLabel;
405378
406379public
407380 { The constructor must be overriden so the label can be created }
408381 constructor Create(AOwner: TComponent); override;
409382
410383published
411384 { Make the label visible in the IDE }
412385 property EmbeddedLabel: TLabel read FEmbeddedLabel;
413386end;
414387
415388implementation
416389
417390constructor TEnhancedPanel.Create(AOwner: TComponent);
418391begin
419392 inherited Create(AOwner);
420393
421394 // Set default width and height
422395 with GetControlClassDefaultSize do
423396 SetInitialBounds(0, 0, CX, CY);
424397
425398 // Add the embedded label
426399 FEmbeddedLabel := TLabel.Create(Self); // Add the embedded label
427400 FEmbeddedLabel.Parent := self; // Show the label in the panel
428401 FEmbeddedLabel.SetSubComponent(true); // Tell the IDE to store the modified properties
429402 FLabel.Name := 'EmbeddedLabel';
430403 FLabel.Caption := 'Howdy World!';
431404
432405 // Make sure the embedded label can not be selected/deleted within the IDE
433406 FLabel.ControlStyle := FLabel.ControlStyle - [csNoDesignSelectable];
434407
435408 // Set other properties if necessary
436409 //...
437410
438411end;
439412```
440413
441414## 使用自定义的绘制过程
442415
443416你可以在自己的程序中构造控件的子类,在以下例子中,TMyLabel就定义了一个自定义的绘制过程:
444417
445418``` pascal
446419type
447420 TMyLabel = class(TLabel)
448421 protected
449422 procedure Paint; override;
450423 end;
451424{...}
452425implementation
453426{...}
454427procedure TMyLabel.Paint;
455428begin
456429 // your code to implement Paint, for example
457430 Canvas.TextOut(0,0,Caption);
458431end;
459432```
460433
461434这样一来,一个TMyLabel控件就会使用如上覆盖的方法来绘制。 但是更常见且更推荐的做法是在其中继承父类的绘制过程:
462435
463436``` pascal
464437procedure TMyLabel.Paint;
465438begin
466439
467440 inherited Paint; /////////////////////
468441
469442 // your code to implement Paint, for example
470443 Canvas.TextOut(0,0,Caption);
471444end;
472445```
473446
474447然而,以上例子也存在一定的问题,因为在重绘之后,第二句又会产生新的修改。
475448
476449## 整合控件和IDE
477450
478451### 属性编辑对话框
479452
480453属性编辑对话框在Object Inspector中提供了自定义的属性编辑窗口,对于大多数属性(字符串、字符串表,图像,枚举类型等),已经有默认的编辑窗口,但是如果控件中有特殊形式的属性,那么就需要对应的属性编辑窗口。
481454
482455每一个属性编辑对话框都是TPropertyEditor的子类,或是其后代的子类。They should be registered in the 'Register' procedure by using the function RegisterPropertyEditor from the unit PropEdits. It is a standard to name property editors with the property name followed by 'Property', for example TFieldProperty for the property editor of the TField property.
483456
484457``` pascal
485458 TPropertyEditor = class
486459 public
487460 function AllEqual: Boolean; Virtual;
488461 function AutoFill: Boolean; Virtual;
489462 procedure Edit; Virtual; // Activated by double-clicking the property value
490463 procedure ShowValue; Virtual; // Activated by control-clicking the property value
491464 function GetAttributes: TPropertyAttributes; Virtual;
492465 function GetEditLimit: Integer; Virtual;
493466 function GetName: ShortString; Virtual;
494467 procedure GetProperties(Proc: TGetPropEditProc); Virtual;
495468 function GetHint(HintType: TPropEditHint; x, y: integer): String; Virtual;
496469 function GetDefaultValue: AnsiString; Virtual;
497470 procedure GetValues(Proc: TGetStrProc); Virtual;
498471 procedure SetValue(const NewValue: AnsiString); Virtual;
499472 procedure UpdateSubProperties; Virtual;
500473 function SubPropertiesNeedsUpdate: Boolean; Virtual;
501474 function IsDefaultValue: Boolean; Virtual;
502475 function IsNotDefaultValue: Boolean; Virtual;
503476 // ... shortened
504477 end;
505478```
506479
507480A good example for a property editor is the TFont property editor.
508481
509482One of the most common cases for property editors is properties which are classes. Because classes have many fields and can have a variety of formats, it's not possible for Lazarus to have the object inspector edit field able to edit it, like is done for strings and numeric types.
510483
511484For classes, a convention is to have the value field show permanently the name of the class in parentheses, for example "(TFont)" and the "..." button shows a dialog to edit this class. This behaviour, except for the dialog, is implemented by a standard property editor for classes called TClassPropertyEditor, which can be inherited from when writing property editors for classes:
512485
513486``` pascal
514487TClassPropertyEditor = class(TPropertyEditor)
515488public
516489 constructor Create(Hook: TPropertyEditorHook; APropCount: Integer); Override;
517490 function GetAttributes: TPropertyAttributes; Override;
518491 procedure GetProperties(Proc: TGetPropEditProc); Override;
519492 function GetValue: AnsiString; Override;
520493 property SubPropsTypeFilter: TTypeKinds Read FSubPropsTypeFilter
521494 Write SetSubPropsTypeFilter
522495 Default tkAny;
523496end;
524497```
525498
526499Going back to the TFont example, inheriting from TClassPropertyEditor already offers part of the desired behavior and then the TFontPropertyEditor class only needs to implement showing the dialog in the Edit method and set the attributes for the editor:
527500
528501``` pascal
529502 TFontPropertyEditor = class(TClassPropertyEditor)
530503 public
531504 procedure Edit; Override;
532505 function GetAttributes: TPropertyAttributes; Override;
533506 end;
534507
535508procedure TFontPropertyEditor.Edit;
536509var
537510 FontDialog: TFontDialog;
538511begin
539512 FontDialog := TFontDialog.Create(NIL);
540513 try
541514 FontDialog.Font := TFont(GetObjectValue(TFont));
542515 FontDialog.Options := FontDialog.Options + [fdShowHelp, fdForceFontExist];
543516 if FontDialog.Execute then SetPtrValue(FontDialog.Font);
544517 finally
545518 FontDialog.Free;
546519 end;
547520end;
548521
549522function TFontPropertyEditor.GetAttributes: TPropertyAttributes;
550523begin
551524 Result := [paMultiSelect, paSubProperties, paDialog, paReadOnly];
552525end;
553526```
554527
555528### 组件编辑器
556529
557530组件编辑器控制在设计窗口中组件的右键和双击行为。
558531
559532Each component editor is a class, which should descend from TComponentEditor or one of its descendents and implement methods from this base class. They should be registered in the 'Register' procedure by using the function RegisterComponentEditor from the unit ComponentEditors. It is a standard to name component editors with the component name followed by 'Editor', for example TStringGridComponentEditor for the property editor of the TStringGrid component. Although user component editors should be based in TComponentEditor, most of its methods are actually from an ancestor, so it is necessary to also know TBaseComponentEditor:
560533
561534``` pascal
562535 TBaseComponentEditor = class
563536 protected
564537 public
565538 constructor Create(AComponent: TComponent;
566539 ADesigner: TComponentEditorDesigner); Virtual;
567540 procedure Edit; Virtual; Abstract;
568541 procedure ExecuteVerb(Index: Integer); Virtual; Abstract;
569542 function GetVerb(Index: Integer): String; Virtual; Abstract;
570543 function GetVerbCount: Integer; Virtual; Abstract;
571544 procedure PrepareItem(Index: Integer; const AnItem: TMenuItem); Virtual; Abstract;
572545 procedure Copy; Virtual; Abstract;
573546 function IsInInlined: Boolean; Virtual; Abstract;
574547 function GetComponent: TComponent; Virtual; Abstract;
575548 function GetDesigner: TComponentEditorDesigner; Virtual; Abstract;
576549 function GetHook(out Hook: TPropertyEditorHook): Boolean; Virtual; Abstract;
577550 procedure Modified; Virtual; Abstract;
578551 end;
579552```
580553
581554The most important method of a component editor is Edit, which is called when the component is double clicked. When the context menu for the component is invoked the GetVerbCount and GetVerb methods are called to build the menu. If one of the verbs (which means menu items in this case) are selected, ExecuteVerb is called. There is a default component editor (TDefaultEditor) which implements Edit to search the properties of the component for the most appropriate one to be edited. It usually chooses an event, which is edited by adding it's skeleton code in the code editor and setting the cursor to be in place to add code for it.
582555
583556Other important methods from TBaseComponentEditor are:
584557
585558- **ExecuteVerb(Index)** - Executes one of the extra menu items placed on the right-click popup menu;
586559- **GetVerb** – To return the name of each extra popup menu item. Note that it is the responsibility of the component editor to place special menu item caption characters like & to create a keyboard accelerator and "-" to create a separator;
587560- **GetVerbCount** – Returns the amount of items to be added to the popup menu. The index for the routines GetVerb and ExecuteVerb is zero based, going from 0 to GetVerbCount – 1;
588561- **PrepareItem** – Called for each verb after the menu item was created. Allows the menu item to be customized such as by adding subitems, adding a checkbox or even hiding it by setting Visible to false;
589562- **Copy** - Called when the component is being copied to the clipboard. The component data for use by Lazarus will always be added and cannot be modified. This method is instead for adding a different kind of clipboard information to paste the component in other applications, but which won't affect the Lazarus paste.
590563
591564A simple and interesting example is the TCheckListBox component editor which creates a dialog to edit. More convenient than implementing all methods from TBaseComponentEditor is inheriting from TComponentEditor, and this is what TCheckListBoxEditor does. This base class adds empty implementations for most methods and some default ones for others. For Edit it calls ExecuteVerb(0), so if the first item will be identical to the double-click action, which is a convention for editor, there is no need to implement Edit. This basic action for the double-click and first menu item is often a dialog, and for TCheckListBox this is also done:
592565
593566``` pascal
594567 TCheckListBoxComponentEditor = class(TComponentEditor)
595568 protected
596569 procedure DoShowEditor;
597570 public
598571 procedure ExecuteVerb(Index: Integer); override;
599572 function GetVerb(Index: Integer): String; override;
600573 function GetVerbCount: Integer; override;
601574 end;
602575
603576procedure TCheckGroupComponentEditor.DoShowEditor;
604577var
605578 Dlg: TCheckGroupEditorDlg;
606579begin
607580 Dlg := TCheckGroupEditorDlg.Create(NIL);
608581 try
609582 // .. shortened
610583 Dlg.ShowModal;
611584 // .. shortened
612585 finally
613586 Dlg.Free;
614587 end;
615588end;
616589
617590procedure TCheckGroupComponentEditor.ExecuteVerb(Index: Integer);
618591begin
619592 case Index of
620593 0: DoShowEditor;
621594 end;
622595end;
623596
624597function TCheckGroupComponentEditor.GetVerb(Index: Integer): String;
625598begin
626599 Result := 'CheckBox Editor...';
627600end;
628601
629602function TCheckGroupComponentEditor.GetVerbCount: Integer;
630603begin
631604 Result := 1;
632605end;
633606```
634607
635608## Design-time component debugging
636609
637610To catch design-time-errors in a (newly created) *dbgcomponent*:
638611
639612- open project `C:\lazarus\ide\lazarus.lpi`;
640613- run project;
641614- set breakpoint in *dbgcomponent* in main (1st) app;
642615- use *dbgcomponent* pascal code in second app;
643616- step through design-time component code in debug-session; do whatever is necessary;
644617
645618## See also
646619
647620- [[Extending the IDE]]
648621- [[Lazarus Packages]] - A guide for creating a package under Lazarus
649622- [[Components and Code examples]]
650623- [[Lazarus Components]]
651624- [[IDE Window: Add to Package]]
652625- [[IDE Window: Create new package component]]
653626
654627You can post questions regarding this page [here](http://forum.lazarus.freepascal.org/index.php/topic,5789.0.html)
655628
656629---
657630*From the Free Pascal wiki, page "How To Write Lazarus Component/zh CN", as it stood on 2021-05-30 after a change by Apiglio, 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).*
658631Category: Code
659632Category: Component Creation
660633Category: Lazarus
661634Category: Packages
662635Category: Tutorials

Back to history