Sign in Join
Wiki copytemporary

How To Write Lazarus Component: what changed

Revision 2 to 3. 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
77![](/wiki-images/7/7c/Package_menu.png)
88
99- 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'.
1010
1111- Congratulations: You have just created your first package!
1212
1313![](/wiki-images/3/37/How_to_write_lazarus_component_package_maker.png)
1414
1515## Step 2: Create the unit
1616
1717You can create a new unit or use an existing file. Both options are described below.
1818
1919### Create a new unit
2020
2121- Use the **Add button \> New component**.
2222
2323![](/wiki-images/5/56/Package_new_component.png)
2424
2525- Choose a component like TComboBox.
2626- Choose *customcontrol1.pas* as **Unit File Name** and *CustomControl1* as **Unit Name**.
2727- You can now give to the component a icon and determine on which palette the component should appear later in the Lazarus-IDE.
2828- Click **Create New Component**.
2929
3030``` pascal
3131unit CustomControl1;
3232
3333{$mode objfpc}{$H+}
3434
3535interface
3636
3737uses
3838 Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls;
3939
4040type
4141 TCustomControl1 = class(TComboBox)
4242 private
4343 { Private declarations }
4444 protected
4545 { Protected declarations }
4646 public
4747 { Public declarations }
4848 published
4949 { Published declarations }
5050 end;
5151
5252procedure Register;
5353
5454implementation
5555
5656procedure Register;
5757begin
5858 RegisterComponents('Standard', [TCustomControl1]);
5959end;
6060
6161end.
6262```
6363
6464- 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.
6565- 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**:
6666
6767![](/wiki-images/3/36/Package_install.png)
6868
6969- Now the IDE will ask you, if the IDE should be rebuilt. Say yes.
7070
7171![](/wiki-images/9/9b/Package_rebuild.png)
7272
7373- 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.
7474
7575![](/wiki-images/3/35/Package_installed.png)
7676
7777> **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.
7878>
7979> 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.
8080
8181> **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.
8282
8383### Add an existing unit
8484
8585If you already had a unit you can add it to the package:
8686
8787![](/wiki-images/1/11/Package_existing_unit.png)
8888
8989- 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.
9090- 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).
9191- Click the **Add** button again, go to the **New Requirement** tab. In the Package name select **LCL** and click OK.
9292
9393The end result should look like this:
9494
9595![](/wiki-images/d/d9/How_to_write_lazarus_component_package_maker_done.png)
9696
9797- Click under the Files tree in the Package Manager. In the File Properties, make sure *Register unit* is checked.
9898- Click the Options button. Select the IDE Integration tab. In Package Type, make sure Designtime and Runtime is selected.
9999- Click the Compile button to check to see that the files compile without errors.
100100- Click the Install button. Lazarus will rebuild and restart automatically.
101101
102102The component is created and ready to be used:
103103
104104![](/wiki-images/3/3c/How_to_write_lazarus_component_created.png)
105105
106106## Step 3: Create icons for the package
107107
108108Each 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.
109109
110110FPC is capable of using standard resource files `.rc` or compiled resources `.res` since fpc version 2.6. See [[Lazarus Resources|Lazarus_Resources#FPC_resources]]
111111
112112The `.lrs`: see below.
113113
114114### Using the Lazarus Image Editor
115115
116116You can use [[Lazarus Image Editor]] to create the icons in .lrs format.
117117
118118### Using lazres
119119
120120`lazres` is usually located in the Lazarus tools directory.
121121
122122You may need to compile [[lazres]] at first use. Simply open the *lazres.lpi* in the IDE and click **run \> build** in the menu.
123123
124124**Create lrs file**
125125
126126To create the lrs file run:
127127
128128`~/lazarus/tools/lazres samplepackage.lrs TMyCom.png`
129129
130130or, if high-resolution images are available
131131
132132`~/lazarus/tools/lazres samplepackage.lrs TMyCom.png TMyCom_150.pgn TMyCom_200.png`
133133
134134Where
135135
136136- *samplepackage* is the name of your package
137137- *TMyCom* is the name of your component! The name of the image must match the name of your component!
138138
139139You 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 ...
140140
141141**Sample**
142142
143143Following is a sample of the resulting *samplepackage.lrs* file.
144144
145145 LazarusResources.Add('TMyCom','PNG',[
146146 #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
147147 +#0#0#0#4'gAMA'#0#0#177#143#11#252'a'#5#0#0#0'|IDAT8O'#237#212#209#10#192' '#8
148148 +#5'P'#247#231#251's'#215#138#133#164#166'\'#220#195'`'#209'c'#157'L'#173#131
149149 +#153#169'd4'#168'dP'#137'r_'#235'5'#136'@Zmk'#16'd9'#144#176#232#164'1'#247
150150 +'I'#8#160'IL'#206'C'#179#144#12#199#140'.'#134#244#141'~'#168#247#209'S~;'#29
151151 +'V+'#196#201'^'#10#15#150'?'#255#18#227#206'NZ>42'#181#159#226#144#15'@'#201
152152 +#148#168'e'#224'7f<@4'#130'u_YD'#23#213#131#134'Q]'#158#188#135#0#0#0#0'IEND'
153153 +#174'B`'#130
154154 ]);
155155
156156Make 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
157157
158158``` pascal
159159initialization
160160 {$I samplepackage.lrs}
161161```
162162
163163**Create res file**
164164
165165lazres can also create a compiled resource file, just specify a file with the .res extension, e.g.:
166166
167167`~/lazarus/tools/lazres samplepackage.`**`res`**` TMyCom.png`
168168
169169or, with high-res icons:
170170
171171`~/lazarus/tools/lazres samplepackage.`**`res`**` TMyCom.png TMyCom_150.png TMyCom_200.png`
172172
173173In this case, instead of including the lrs file in the initialization section, just include the resource file anywhere in your component source file
174174
175175``` pascal
176176 {$R samplepackage.res}
177177```
178178
179179### Using glazres
180180
181181**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.
182182
183183## Recompiling Packages
184184
185185You 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.
186186
187187## Removing Packages
188188
189189- To remove installed components: on the IDE menu, click Package \> Configure installed packages. The following image shows the Installed Packages tool.
190190
191-![`Installed Components`](/wiki-images/0/0e/How_to_write_lazarus_component_installed_components.png)
191+```text
192+Installed Components
193+```
192194
193195- Select the package you want to uninstall and click Uninstall selection.
194196
195197If 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.
196198
197199## Enhancing mycom.pas
198200
199201- 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.
200202- The *OnChange2* shows how to create events
201203- The *OnSample* shows how to create custom events
202204- *MyText* and *MyText2* shows different ways to write properties.
203205- You can use TComboBox instead of TCustomComboBox as the base class, which publishes all properties as TComboBox.
204206- 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.
205207
206208``` pascal
207209unit mycom;
208210
209211{$mode objfpc}{$H+}
210212
211213interface
212214
213215uses
214216 Classes, SysUtils, StdCtrls, Forms, Dialogs,
215217 LCLType,LCLIntf,lresources,LCLProc;
216218
217219type
218220
219221 TSampleEvent = procedure(MyText: String) of Object;
220222
221223 TMyCom = class (TCustomComboBox)
222224 private
223225 FMyText: String;
224226 FOnChange2: TNotifyEvent;
225227 FOnSample: TSampleEvent;
226228 public
227229 constructor Create(TheOwner: TComponent); override;
228230 procedure CreateWnd; override;
229231 procedure Change; override;
230232 protected
231233 function GetMyText2: String;
232234 procedure SetMyText2(MyText: String);
233235 published
234236 property MyText: String read FMyText write FMyText;
235237 property MyText2: String read GetMyText2 write SetMyText2;
236238 property OnChange2: TNotifyEvent read FOnChange2 write FOnChange2;
237239 property OnSample: TSampleEvent read FOnSample write FOnSample;
238240
239241 // properties from TComboBox
240242 property Align;
241243 property Anchors;
242244 property ArrowKeysTraverseList;
243245 property AutoComplete;
244246 property AutoCompleteText;
245247 property AutoDropDown;
246248 property AutoSelect;
247249 property AutoSize;
248250 property BidiMode;
249251 property BorderSpacing;
250252 property CharCase;
251253 property Color;
252254 property Ctl3D;
253255 property Constraints;
254256 property DragCursor;
255257 property DragMode;
256258 property DropDownCount;
257259 property Enabled;
258260 property Font;
259261 property ItemHeight;
260262 property ItemIndex;
261263 property Items;
262264 property ItemWidth;
263265 property MaxLength;
264266 property OnChange;
265267 property OnChangeBounds;
266268 property OnClick;
267269 property OnCloseUp;
268270 property OnContextPopup;
269271 property OnDblClick;
270272 property OnDragDrop;
271273 property OnDragOver;
272274 property OnDrawItem;
273275 property OnEndDrag;
274276 property OnDropDown;
275277 property OnEditingDone;
276278 property OnEnter;
277279 property OnExit;
278280 property OnGetItems;
279281 property OnKeyDown;
280282 property OnKeyPress;
281283 property OnKeyUp;
282284 property OnMeasureItem;
283285 property OnMouseDown;
284286 property OnMouseMove;
285287 property OnMouseUp;
286288 property OnStartDrag;
287289 property OnSelect;
288290 property OnUTF8KeyPress;
289291 property ParentBidiMode;
290292 property ParentColor;
291293 property ParentCtl3D;
292294 property ParentFont;
293295 property ParentShowHint;
294296 property PopupMenu;
295297 property ReadOnly;
296298 property ShowHint;
297299 property Sorted;
298300 property Style;
299301 property TabOrder;
300302 property TabStop;
301303 property Text;
302304 property Visible;
303305 end;
304306
305307procedure Register;
306308
307309implementation
308310
309311procedure Register;
310312begin
311313 RegisterComponents('Sample',[TMyCom]);
312314end;
313315
314316constructor TMyCom.Create(TheOwner: TComponent);
315317begin
316318 inherited Create(TheOwner);
317319 Self.Style := csDropDownList;
318320end;
319321
320322procedure TMyCom.CreateWnd;
321323begin
322324 inherited CreateWnd;
323325 Items.Assign(Screen.Fonts);
324326end;
325327
326328procedure TMyCom.Change;
327329begin
328330 inherited;
329331 if Assigned(FOnChange2) then FOnChange2(Self);
330332 if Assigned(FOnSample) then FOnSample(FMyText);
331333end;
332334
333335function TMyCom.GetMyText2: String;
334336begin
335337 Result:=FMyText;
336338end;
337339
338340procedure TMyCom.SetMyText2(MyText: String);
339341begin
340342 FMyText:=MyText;
341343end;
342344
343345initialization
344346 {$I samplepackage.lrs}
345347
346348end.
347349```
348350
349351Also 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:
350352
351353``` pascal
352354Published
353355...
354356 property Height: Integer;
355357 property Width: Integer;
356358...
357359```
358360
359361## Using embedded (visual) components
360362
361363It's possible to use standard components embedded in your own components (look for example at TLabeledEdit or TButtonPanel).
362364
363365Let'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:
364366
365367- Add a private attribute for the label component (FEmbeddedLabel: TLabel;).
366368- Add a published read-only property for the label component (property EmbeddedLabel: TLabel read FEmbeddedLabel;)
367369- Create the label in the component's (overridden) constructor (FEmbeddedLabel := TLabel.Create(self); )
368370- Set the parent of the component (FEmbeddedLabel.Parent := self;)
369371- 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);).
370372
371373To sum it up you would get something like this (only the essential parts are shown):
372374
373375``` pascal
374376TEnhancedPanel = class(TCustomControl)
375377private
376378 { The new attribute for the embedded label }
377379 FEmbeddedLabel: TLabel;
378380
379381public
380382 { The constructor must be overriden so the label can be created }
381383 constructor Create(AOwner: TComponent); override;
382384
383385published
384386 { Make the label visible in the IDE }
385387 property EmbeddedLabel: TLabel read FEmbeddedLabel;
386388end;
387389
388390implementation
389391
390392constructor TEnhancedPanel.Create(AOwner: TComponent);
391393begin
392394 inherited Create(AOwner);
393395
394396 // Set default width and height
395397 with GetControlClassDefaultSize do
396398 SetInitialBounds(0, 0, CX, CY);
397399
398400 // Add the embedded label
399401 FEmbeddedLabel := TLabel.Create(Self); // Add the embedded label
400402 FEmbeddedLabel.Parent := self; // Show the label in the panel
401403 FEmbeddedLabel.SetSubComponent(true); // Tell the IDE to store the modified properties
402404 FLabel.Name := 'EmbeddedLabel';
403405 FLabel.Caption := 'Howdy World!';
404406
405407 // Make sure the embedded label can not be selected/deleted within the IDE
406408 FLabel.ControlStyle := FLabel.ControlStyle - [csNoDesignSelectable];
407409
408410 // Set other properties if necessary
409411 //...
410412
411413end;
412414```
413415
414416The 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)
415417
416418## Using custom paint procedure
417419
418420You can always subclass a component inside your program. For example, this implements a custom Paint procedure to a TLabel:
419421
420422``` pascal
421423type
422424 TMyLabel = class(TLabel)
423425 protected
424426 procedure Paint; override;
425427 end;
426428{...}
427429implementation
428430{...}
429431procedure TMyLabel.Paint;
430432begin
431433 // your code to implement Paint, for example
432434 Canvas.TextOut(0,0,Caption);
433435end;
434436```
435437
436438Now you can create a MyLabel inside your program, at run time, with that overridden Paint procedure instead of the standard one.
437439
438440For most components, and for most methods, it would be recommendable to call inherited procedure inside it:
439441
440442``` pascal
441443procedure TMyLabel.Paint;
442444begin
443445
444446 inherited Paint; /////////////////////
445447
446448 // your code to implement Paint, for example
447449 Canvas.TextOut(0,0,Caption);
448450end;
449451```
450452
451453However, inherited behavior is not desirable in this case, since the second writing action would overlap the first (inherited) one.
452454
453455## Integrating the component with the IDE
454456
455457### Property editors
456458
457459Property 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.
458460
459461Each 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.
460462
461463``` pascal
462464 TPropertyEditor = class
463465 public
464466 function AllEqual: Boolean; Virtual;
465467 function AutoFill: Boolean; Virtual;
466468 procedure Edit; Virtual; // Activated by double-clicking the property value
467469 procedure ShowValue; Virtual; // Activated by control-clicking the property value
468470 function GetAttributes: TPropertyAttributes; Virtual;
469471 function GetEditLimit: Integer; Virtual;
470472 function GetName: ShortString; Virtual;
471473 procedure GetProperties(Proc: TGetPropEditProc); Virtual;
472474 function GetHint(HintType: TPropEditHint; x, y: integer): String; Virtual;
473475 function GetDefaultValue: AnsiString; Virtual;
474476 procedure GetValues(Proc: TGetStrProc); Virtual;
475477 procedure SetValue(const NewValue: AnsiString); Virtual;
476478 procedure UpdateSubProperties; Virtual;
477479 function SubPropertiesNeedsUpdate: Boolean; Virtual;
478480 function IsDefaultValue: Boolean; Virtual;
479481 function IsNotDefaultValue: Boolean; Virtual;
480482 // ... shortened
481483 end;
482484```
483485
484486A good example for a property editor is the TFont property editor.
485487
486488One 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.
487489
488490For 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:
489491
490492``` pascal
491493TClassPropertyEditor = class(TPropertyEditor)
492494public
493495 constructor Create(Hook: TPropertyEditorHook; APropCount: Integer); Override;
494496 function GetAttributes: TPropertyAttributes; Override;
495497 procedure GetProperties(Proc: TGetPropEditProc); Override;
496498 function GetValue: AnsiString; Override;
497499 property SubPropsTypeFilter: TTypeKinds Read FSubPropsTypeFilter
498500 Write SetSubPropsTypeFilter
499501 Default tkAny;
500502end;
501503```
502504
503505Going 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:
504506
505507``` pascal
506508 TFontPropertyEditor = class(TClassPropertyEditor)
507509 public
508510 procedure Edit; Override;
509511 function GetAttributes: TPropertyAttributes; Override;
510512 end;
511513
512514procedure TFontPropertyEditor.Edit;
513515var
514516 FontDialog: TFontDialog;
515517begin
516518 FontDialog := TFontDialog.Create(NIL);
517519 try
518520 FontDialog.Font := TFont(GetObjectValue(TFont));
519521 FontDialog.Options := FontDialog.Options + [fdShowHelp, fdForceFontExist];
520522 if FontDialog.Execute then SetPtrValue(FontDialog.Font);
521523 finally
522524 FontDialog.Free;
523525 end;
524526end;
525527
526528function TFontPropertyEditor.GetAttributes: TPropertyAttributes;
527529begin
528530 Result := [paMultiSelect, paSubProperties, paDialog, paReadOnly];
529531end;
530532```
531533
532534The 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)
533535
534536### Component editors
535537
536538Component editors control the behavior of right-clicking and double clicking components in the form designer.
537539
538540Each 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:
539541
540542``` pascal
541543 TBaseComponentEditor = class
542544 protected
543545 public
544546 constructor Create(AComponent: TComponent;
545547 ADesigner: TComponentEditorDesigner); Virtual;
546548 procedure Edit; Virtual; Abstract;
547549 procedure ExecuteVerb(Index: Integer); Virtual; Abstract;
548550 function GetVerb(Index: Integer): String; Virtual; Abstract;
549551 function GetVerbCount: Integer; Virtual; Abstract;
550552 procedure PrepareItem(Index: Integer; const AnItem: TMenuItem); Virtual; Abstract;
551553 procedure Copy; Virtual; Abstract;
552554 function IsInInlined: Boolean; Virtual; Abstract;
553555 function GetComponent: TComponent; Virtual; Abstract;
554556 function GetDesigner: TComponentEditorDesigner; Virtual; Abstract;
555557 function GetHook(out Hook: TPropertyEditorHook): Boolean; Virtual; Abstract;
556558 procedure Modified; Virtual; Abstract;
557559 end;
558560```
559561
560562The 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.
561563
562564Other important methods from TBaseComponentEditor are:
563565
564566- **ExecuteVerb(Index)** - Executes one of the extra menu items placed on the right-click popup menu;
565567- **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;
566568- **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;
567569- **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;
568570- **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.
569571
570572A 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:
571573
572574``` pascal
573575 TCheckListBoxComponentEditor = class(TComponentEditor)
574576 protected
575577 procedure DoShowEditor;
576578 public
577579 procedure ExecuteVerb(Index: Integer); override;
578580 function GetVerb(Index: Integer): String; override;
579581 function GetVerbCount: Integer; override;
580582 end;
581583
582584procedure TCheckGroupComponentEditor.DoShowEditor;
583585var
584586 Dlg: TCheckGroupEditorDlg;
585587begin
586588 Dlg := TCheckGroupEditorDlg.Create(NIL);
587589 try
588590 // .. shortened
589591 Dlg.ShowModal;
590592 // .. shortened
591593 finally
592594 Dlg.Free;
593595 end;
594596end;
595597
596598procedure TCheckGroupComponentEditor.ExecuteVerb(Index: Integer);
597599begin
598600 case Index of
599601 0: DoShowEditor;
600602 end;
601603end;
602604
603605function TCheckGroupComponentEditor.GetVerb(Index: Integer): String;
604606begin
605607 Result := 'CheckBox Editor...';
606608end;
607609
608610function TCheckGroupComponentEditor.GetVerbCount: Integer;
609611begin
610612 Result := 1;
611613end;
612614```
613615
614616## Design-time component debugging
615617
616618To catch design-time-errors in a (newly created) *dbgcomponent*:
617619
618620- open project `C:\lazarus\ide\lazarus.lpi`;
619621- run project;
620622- set breakpoint in *dbgcomponent* in main (1st) app;
621623- use *dbgcomponent* pascal code in second app;
622624- step through design-time component code in debug-session; do whatever is necessary;
623625
624626## See also
625627
626628- [[Extending the IDE]]
627629- [[Lazarus Packages]] - A guide for creating a package under Lazarus
628630- [[Components and Code examples]]
629631- [[Lazarus Components]]
630632- [[IDE Window: Add to Package]]
631633- [[IDE Window: Create new package component]]
632634
633635You can post questions regarding this page [here](http://forum.lazarus.freepascal.org/index.php/topic,5789.0.html)
634636
635637---
636638*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).*
637639Category: Code
638640Category: Component Creation
639641Category: Lazarus
640642Category: Packages
641643Category: Tutorials

Back to history