Sign in Join
Wiki copytemporary

How To Write Lazarus Component: what changed

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

OldNewLine
11This is a guide on how to build components.
22
33## Step 1: Create the package
44
55- On the Lazarus IDE menu, click **Package \> New Package** to run the Package Manager.
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- A **Save** file dialog will appear. Chose a folder and a file name and press save. If the IDE prompts for using lowercase filenames press 'yes'.
1310
1411- Congratulations: You have just created your first 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## Step 2: Create the unit
2216
2317You can create a new unit or use an existing file. Both options are described below.
2418
2519### Create a new unit
2620
2721- Use the **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- Choose a component like TComboBox.
3526- Choose *customcontrol1.pas* as **Unit File Name** and *CustomControl1* as **Unit Name**.
3627- You can now give to the component a icon and determine on which palette the component should appear later in the Lazarus-IDE.
3728- Click **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- Now add the code for your component. Resist the temptation to install the new component into the IDE at this early stage. It is very likely that the new component is still buggy. Since the new component will become a part of the IDE it can crash the IDE, it may even be possible that the IDE does not start any more. Test your component excessively.
7465- When the component is tested as much as possible install the package by clicking opening the package in "Package" \> "Install/uninstall packages", click the "Compile" button at the top of the package editor, and then **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- Now the IDE will ask you, if the IDE should be rebuilt. Say yes.
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 automatically restarts and you can see your new component in the component palette. Congratulations: you have just installed your first package with your first package component.
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:** If things go wrong because the new component is still buggy and Lazarus cannot be started again you delete the new lazarus.exe and rename the backup copy of the previous version, lazarus.old.exe, back to lazarus.exe.
10082
10183### Add an existing unit
10284
10385If you already had a unit you can add it to the package:
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- Click the **Add** button, go to the **Add Files** tab. At the Unit file name, browse to your existing file. Click **Add files to package**. If the package manager complains that the unit is not in the unit path, click yes to add the directory to the unit path.
11190- Click the **Add** button again, go to the **Add Files** tab, browse to the *.lrs* file and click OK (See Step 3 about creating this icon file).
11291- Click the **Add** button again, go to the **New Requirement** tab. In the Package name select **LCL** and click OK.
11392
11493The end result should look like this:
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
126102The component is created and ready to be used:
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## Step 3: Create icons for the package
134107
135108Each component which should have an icon on the component palette requires images files packaged to a resource file. It is highly recommended to create the images as PNG files with alpha channel. The images should be 24x24 pixels in size. However, since Lazarus 1.8+ supports scaling of the icons at higher screen resolutions, you should also create images at 36x36 and 48x48 pixels which will be displayed at 150% and 200% resolutions, respectively). All images must have the same filename as the component, and the for extra-resolution images the appendices "_150" and "_200" must be added to the base filename to indicate the scaling factor. As an example, if your component is called `TMySuperEdit` you should prepare images named as `tmysuperedit.png`, `tmysuperedit_150.png`, and `tmysuperedit_200.png`. To simplify art work, it is recommended to draw the images as SVG vector images from which the destination sizes can be exported.
136109
137110FPC is capable of using standard resource files `.rc` or compiled resources `.res` since fpc version 2.6. See [[Lazarus Resources|Lazarus_Resources#FPC_resources]]
138111
139112The `.lrs`: see below.
140113
141114### Using the Lazarus Image Editor
142115
143116You can use [[Lazarus Image Editor]] to create the icons in .lrs format.
144117
145118### Using lazres
146119
147120`lazres` is usually located in the Lazarus tools directory.
148121
149122You may need to compile [[lazres]] at first use. Simply open the *lazres.lpi* in the IDE and click **run \> build** in the menu.
150123
151124**Create lrs file**
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**Sample**
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**Create res file**
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### Using 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## Recompiling Packages
211184
212185You need to rebuild the package every time you make changes to the *mycom.pas* file. To rebuild the package, open the *samplepackage.lpk* file in the Package Manager and click the Install button.
213186
214187## Removing Packages
215188
216189- To remove installed components: on the IDE menu, click Package \> Configure installed packages. The following image shows the Installed Packages tool.
217190
218191![`Installed Components`](/wiki-images/0/0e/How_to_write_lazarus_component_installed_components.png)
219192
220193- Select the package you want to uninstall and click Uninstall selection.
221194
222195If something goes wrong with a package (e.g. package directory is deleted without first uninstalling it), Lazarus may not allow you to uninstall packages. To fix the problem, at the IDE menu click Tools \> Build Lazarus. Lazarus will rebuild all packages and restart. You should now be able to uninstall problematic packages.
223196
224197## Enhancing mycom.pas
225198
226199- The code in *mycom.pas* above gives you the basics on what you need to create a component. The following is an enhanced version with some tips on how to write procedures and events for components.
227200- The *OnChange2* shows how to create events
228201- The *OnSample* shows how to create custom events
229202- *MyText* and *MyText2* shows different ways to write properties.
230203- You can use TComboBox instead of TCustomComboBox as the base class, which publishes all properties as TComboBox.
231204- If TCustomComboBox is used as the base class, you'll notice a lot of properties and events will be missing in the IDE Object Inspector. To add those properties and events, just copy and paste the properties as listed below *// properties from TComboBox*. These list of properties can be got from the TComboBox declaration in the StdCtrls unit. Omit any property which you want to handle yourself.
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
376349Also you will notice that some undeclared and possibly unwanted elements exist in the IDE Object Inspector. To remove those of them, which you do not need, you can redeclare them in the *Published* section as simple variables. For example:
377350
378351``` pascal
379352Published
380353...
381354 property Height: Integer;
382355 property Width: Integer;
383356...
384357```
385358
386359## Using embedded (visual) components
387360
388361It's possible to use standard components embedded in your own components (look for example at TLabeledEdit or 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. Now do the following to add a TLabel to the component:
391364
392365- Add a private attribute for the label component (FEmbeddedLabel: TLabel;).
393366- Add a published read-only property for the label component (property EmbeddedLabel: TLabel read FEmbeddedLabel;)
394367- Create the label in the component's (overridden) constructor (FEmbeddedLabel := TLabel.Create(self); )
395368- Set the parent of the component (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
398371To sum it up you would get something like this (only the essential parts are shown):
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
441414The reference for Delphi (Embarcadero): [Creating a Styled FireMonkey Component by Extending an Existing Component](https://docwiki.embarcadero.com/RADStudio/Alexandria/en/Creating_a_Styled_FireMonkey_Component_by_Extending_an_Existing_Component)
442415
443416## Using custom paint procedure
444417
445418You can always subclass a component inside your program. For example, this implements a custom Paint procedure to a TLabel:
446419
447420``` pascal
448421type
449422 TMyLabel = class(TLabel)
450423 protected
451424 procedure Paint; override;
452425 end;
453426{...}
454427implementation
455428{...}
456429procedure TMyLabel.Paint;
457430begin
458431 // your code to implement Paint, for example
459432 Canvas.TextOut(0,0,Caption);
460433end;
461434```
462435
463436Now you can create a MyLabel inside your program, at run time, with that overridden Paint procedure instead of the standard one.
464437
465438For most components, and for most methods, it would be recommendable to call inherited procedure inside it:
466439
467440``` pascal
468441procedure TMyLabel.Paint;
469442begin
470443
471444 inherited Paint; /////////////////////
472445
473446 // your code to implement Paint, for example
474447 Canvas.TextOut(0,0,Caption);
475448end;
476449```
477450
478451However, inherited behavior is not desirable in this case, since the second writing action would overlap the first (inherited) one.
479452
480453## Integrating the component with the IDE
481454
482455### Property editors
483456
484457Property editors provide custom dialogs to edit properties in the object inspector. For most properties, like strings, string lists, images, enumerated types and others, there are already default property editors, but if a custom component has a special kind of property it may require a custom dialog to edit the property.
485458
486459Each property editor is a class, which should descend from TPropertyEditor or one of its descendents and implement methods from this base class. 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.
487460
488461``` pascal
489462 TPropertyEditor = class
490463 public
491464 function AllEqual: Boolean; Virtual;
492465 function AutoFill: Boolean; Virtual;
493466 procedure Edit; Virtual; // Activated by double-clicking the property value
494467 procedure ShowValue; Virtual; // Activated by control-clicking the property value
495468 function GetAttributes: TPropertyAttributes; Virtual;
496469 function GetEditLimit: Integer; Virtual;
497470 function GetName: ShortString; Virtual;
498471 procedure GetProperties(Proc: TGetPropEditProc); Virtual;
499472 function GetHint(HintType: TPropEditHint; x, y: integer): String; Virtual;
500473 function GetDefaultValue: AnsiString; Virtual;
501474 procedure GetValues(Proc: TGetStrProc); Virtual;
502475 procedure SetValue(const NewValue: AnsiString); Virtual;
503476 procedure UpdateSubProperties; Virtual;
504477 function SubPropertiesNeedsUpdate: Boolean; Virtual;
505478 function IsDefaultValue: Boolean; Virtual;
506479 function IsNotDefaultValue: Boolean; Virtual;
507480 // ... shortened
508481 end;
509482```
510483
511484A good example for a property editor is the TFont property editor.
512485
513486One 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.
514487
515488For 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:
516489
517490``` pascal
518491TClassPropertyEditor = class(TPropertyEditor)
519492public
520493 constructor Create(Hook: TPropertyEditorHook; APropCount: Integer); Override;
521494 function GetAttributes: TPropertyAttributes; Override;
522495 procedure GetProperties(Proc: TGetPropEditProc); Override;
523496 function GetValue: AnsiString; Override;
524497 property SubPropsTypeFilter: TTypeKinds Read FSubPropsTypeFilter
525498 Write SetSubPropsTypeFilter
526499 Default tkAny;
527500end;
528501```
529502
530503Going 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:
531504
532505``` pascal
533506 TFontPropertyEditor = class(TClassPropertyEditor)
534507 public
535508 procedure Edit; Override;
536509 function GetAttributes: TPropertyAttributes; Override;
537510 end;
538511
539512procedure TFontPropertyEditor.Edit;
540513var
541514 FontDialog: TFontDialog;
542515begin
543516 FontDialog := TFontDialog.Create(NIL);
544517 try
545518 FontDialog.Font := TFont(GetObjectValue(TFont));
546519 FontDialog.Options := FontDialog.Options + [fdShowHelp, fdForceFontExist];
547520 if FontDialog.Execute then SetPtrValue(FontDialog.Font);
548521 finally
549522 FontDialog.Free;
550523 end;
551524end;
552525
553526function TFontPropertyEditor.GetAttributes: TPropertyAttributes;
554527begin
555528 Result := [paMultiSelect, paSubProperties, paDialog, paReadOnly];
556529end;
557530```
558531
559532The reference for Delphi (Embarcadero): [Creating a Component Editor and a Property Editor for FireMonkey Components](https://docwiki.embarcadero.com/RADStudio/Alexandria/en/Creating_a_Component_Editor_and_a_Property_Editor_for_FireMonkey_Components)
560533
561534### Component editors
562535
563536Component editors control the behavior of right-clicking and double clicking components in the form designer.
564537
565538Each 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:
566539
567540``` pascal
568541 TBaseComponentEditor = class
569542 protected
570543 public
571544 constructor Create(AComponent: TComponent;
572545 ADesigner: TComponentEditorDesigner); Virtual;
573546 procedure Edit; Virtual; Abstract;
574547 procedure ExecuteVerb(Index: Integer); Virtual; Abstract;
575548 function GetVerb(Index: Integer): String; Virtual; Abstract;
576549 function GetVerbCount: Integer; Virtual; Abstract;
577550 procedure PrepareItem(Index: Integer; const AnItem: TMenuItem); Virtual; Abstract;
578551 procedure Copy; Virtual; Abstract;
579552 function IsInInlined: Boolean; Virtual; Abstract;
580553 function GetComponent: TComponent; Virtual; Abstract;
581554 function GetDesigner: TComponentEditorDesigner; Virtual; Abstract;
582555 function GetHook(out Hook: TPropertyEditorHook): Boolean; Virtual; Abstract;
583556 procedure Modified; Virtual; Abstract;
584557 end;
585558```
586559
587560The 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.
588561
589562Other important methods from TBaseComponentEditor are:
590563
591564- **ExecuteVerb(Index)** - Executes one of the extra menu items placed on the right-click popup menu;
592565- **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;
593566- **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;
594567- **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;
595568- **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.
596569
597570A 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:
598571
599572``` pascal
600573 TCheckListBoxComponentEditor = class(TComponentEditor)
601574 protected
602575 procedure DoShowEditor;
603576 public
604577 procedure ExecuteVerb(Index: Integer); override;
605578 function GetVerb(Index: Integer): String; override;
606579 function GetVerbCount: Integer; override;
607580 end;
608581
609582procedure TCheckGroupComponentEditor.DoShowEditor;
610583var
611584 Dlg: TCheckGroupEditorDlg;
612585begin
613586 Dlg := TCheckGroupEditorDlg.Create(NIL);
614587 try
615588 // .. shortened
616589 Dlg.ShowModal;
617590 // .. shortened
618591 finally
619592 Dlg.Free;
620593 end;
621594end;
622595
623596procedure TCheckGroupComponentEditor.ExecuteVerb(Index: Integer);
624597begin
625598 case Index of
626599 0: DoShowEditor;
627600 end;
628601end;
629602
630603function TCheckGroupComponentEditor.GetVerb(Index: Integer): String;
631604begin
632605 Result := 'CheckBox Editor...';
633606end;
634607
635608function TCheckGroupComponentEditor.GetVerbCount: Integer;
636609begin
637610 Result := 1;
638611end;
639612```
640613
641614## Design-time component debugging
642615
643616To catch design-time-errors in a (newly created) *dbgcomponent*:
644617
645618- open project `C:\lazarus\ide\lazarus.lpi`;
646619- run project;
647620- set breakpoint in *dbgcomponent* in main (1st) app;
648621- use *dbgcomponent* pascal code in second app;
649622- step through design-time component code in debug-session; do whatever is necessary;
650623
651624## See also
652625
653626- [[Extending the IDE]]
654627- [[Lazarus Packages]] - A guide for creating a package under Lazarus
655628- [[Components and Code examples]]
656629- [[Lazarus Components]]
657630- [[IDE Window: Add to Package]]
658631- [[IDE Window: Create new package component]]
659632
660633You can post questions regarding this page [here](http://forum.lazarus.freepascal.org/index.php/topic,5789.0.html)
661634
662635---
663636*From the Free Pascal wiki, page "How To Write Lazarus Component", as it stood on 2023-11-07 after a change by Ernesto, 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).*
664637Category: Code
665638Category: Component Creation
666639Category: Lazarus
667640Category: Packages
668641Category: Tutorials

Back to history