Sign in Join
Wiki copytemporary

How To Write Lazarus Component: what changed

Revision 3 to 4. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
1-This is a guide on how to build components.
1+This is a guide on how to build components.
22
33## Step 1: Create the package
44
5-- On the Lazarus IDE menu, click **Package \> New Package** to run the Package Manager.
5+ * On the Lazarus IDE menu, click **Package > New Package** to run the Package Manager.
66
7-![](/wiki-images/7/7c/Package_menu.png)
7+[![package menu.png](/wiki-images/f/fb/package_menu.png)](</File:package_menu.png>)
88
9-- 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'.
9+ * 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
11-- Congratulations: You have just created your first package!
11+ * Congratulations: You have just created your first package!
1212
13-![](/wiki-images/3/37/How_to_write_lazarus_component_package_maker.png)
13+[![Package Maker](/wiki-images/3/37/How_to_write_lazarus_component_package_maker.png)](</File:How_to_write_lazarus_component_package_maker.png> "Package Maker")
1414
1515## Step 2: Create the unit
1616
17-You can create a new unit or use an existing file. Both options are described below.
17+You can create a new unit or use an existing file. Both options are described below.
1818
1919### Create a new unit
2020
21-- Use the **Add button \> New component**.
21+ * Use the **Add button > New component**.
2222
23-![](/wiki-images/5/56/Package_new_component.png)
23+[![package new component.png](/wiki-images/6/60/package_new_component.png)](</File:package_new_component.png>)
2424
25-- Choose a component like TComboBox.
26-- Choose *customcontrol1.pas* as **Unit File Name** and *CustomControl1* as **Unit Name**.
27-- You can now give to the component a icon and determine on which palette the component should appear later in the Lazarus-IDE.
28-- Click **Create New Component**.
25+ * Choose a component like TComboBox.
26+ * Choose _customcontrol1.pas_ as **Unit File Name** and _CustomControl1_ as **Unit Name**.
27+ * You can now give to the component a icon and determine on which palette the component should appear later in the Lazarus-IDE.
28+ * Click **Create New Component**.
2929
30-``` pascal
30+```
3131unit CustomControl1;
32-
33-{$mode objfpc}{$H+}
32+
33+ {$mode objfpc}{$H+}
34+
35+ interface
36+
37+ uses
38+ Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls;
39+
40+ type
41+ TCustomControl1 = class(TComboBox)
42+ private
43+ { Private declarations }
44+ protected
45+ { Protected declarations }
46+ public
47+ { Public declarations }
48+ published
49+ { Published declarations }
50+ end;
51+
52+ procedure Register;
53+
54+ implementation
55+
56+ procedure Register;
57+ begin
58+ RegisterComponents('Standard', [TCustomControl1]);
59+ end;
60+
61+ end.
62+```
3463
35-interface
64+ * 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.
65+ * 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**:
3666
37-uses
38- Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls;
67+[![package install.png](/wiki-images/2/2a/package_install.png)](</File:package_install.png>)
3968
40-type
41- TCustomControl1 = class(TComboBox)
42- private
43- { Private declarations }
44- protected
45- { Protected declarations }
46- public
47- { Public declarations }
48- published
49- { Published declarations }
50- end;
69+ * Now the IDE will ask you, if the IDE should be rebuilt. Say yes.
5170
52-procedure Register;
71+[![package rebuild.png](/wiki-images/6/67/package_rebuild.png)](</File:package_rebuild.png>)
5372
54-implementation
73+ * 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.
5574
56-procedure Register;
57-begin
58- RegisterComponents('Standard', [TCustomControl1]);
59-end;
75+[![package installed.png](/wiki-images/7/79/package_installed.png)](</File:package_installed.png>)
6076
61-end.
62-```
77+![Light bulb](https://upload.wikimedia.org/wikipedia/commons/d/d8/Nuvola_apps_ktip.png) **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.
6378
64-- 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.
65-- 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**:
79+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.
6680
67-![](/wiki-images/3/36/Package_install.png)
81+![Light bulb](https://upload.wikimedia.org/wikipedia/commons/d/d8/Nuvola_apps_ktip.png) **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.
6882
69-- Now the IDE will ask you, if the IDE should be rebuilt. Say yes.
83+### Add an existing unit
7084
71-![](/wiki-images/9/9b/Package_rebuild.png)
85+If you already had a unit you can add it to the package:
7286
73-- 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.
87+[![package existing unit.png](/wiki-images/a/a4/package_existing_unit.png)](</File:package_existing_unit.png>)
7488
75-![](/wiki-images/3/35/Package_installed.png)
89+ * 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.
90+ * 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).
91+ * Click the **Add** button again, go to the **New Requirement** tab. In the Package name select **LCL** and click OK.
7692
77-> **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.
78->
79-> 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.
93+The end result should look like this:
8094
81-> **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.
95+[![Package Maker](/wiki-images/d/d9/How_to_write_lazarus_component_package_maker_done.png)](</File:How_to_write_lazarus_component_package_maker_done.png> "Package Maker")
8296
83-### Add an existing unit
97+ * Click under the Files tree in the Package Manager. In the File Properties, make sure _Register unit_ is checked.
98+ * Click the Options button. Select the IDE Integration tab. In Package Type, make sure Designtime and Runtime is selected.
99+ * Click the Compile button to check to see that the files compile without errors.
100+ * Click the Install button. Lazarus will rebuild and restart automatically.
84101
85-If you already had a unit you can add it to the package:
102+The component is created and ready to be used:
86103
87-![](/wiki-images/1/11/Package_existing_unit.png)
104+[![Component Created](/wiki-images/3/3c/How_to_write_lazarus_component_created.png)](</File:How_to_write_lazarus_component_created.png> "Component Created")
88105
89-- 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.
90-- 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).
91-- Click the **Add** button again, go to the **New Requirement** tab. In the Package name select **LCL** and click OK.
106+## Step 3: Create icons for the package
92107
93-The end result should look like this:
108+Each 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.
94109
95-![](/wiki-images/d/d9/How_to_write_lazarus_component_package_maker_done.png)
110+The images should be 24x24 pixels in size.
96111
97-- Click under the Files tree in the Package Manager. In the File Properties, make sure *Register unit* is checked.
98-- Click the Options button. Select the IDE Integration tab. In Package Type, make sure Designtime and Runtime is selected.
99-- Click the Compile button to check to see that the files compile without errors.
100-- Click the Install button. Lazarus will rebuild and restart automatically.
112+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.
101113
102-The component is created and ready to be used:
114+**NOTE:** If the image does not contain an alpha channel, the colour of the top-left pixel is considered as the transparency channel.
103115
104-![](/wiki-images/3/3c/How_to_write_lazarus_component_created.png)
116+FPC is capable of using standard resource files `.rc` or compiled resources `.res` since fpc version 2.6. See [[Lazarus Resources|Lazarus_Resources#FPC_resources]]
105117
106-## Step 3: Create icons for the package
118+The `.lrs`: see below.
107119
108-Each 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.
120+### Using the Lazarus Image Editor
109121
110-FPC is capable of using standard resource files `.rc` or compiled resources `.res` since fpc version 2.6. See [[Lazarus Resources|Lazarus_Resources#FPC_resources]]
122+You can use [[Lazarus Image Editor]] to create the icons in .lrs format.
111123
112-The `.lrs`: see below.
124+### LRS Explorer
113125
114-### Using the Lazarus Image Editor
126+LRS Explorer [[1]](<https://sourceforge.net/projects/lrsexplorer/>) is a tool with a GUI which can create LRS files from PNG and other image formats.
115127
116-You can use [[Lazarus Image Editor]] to create the icons in .lrs format.
128+After creating a `.lrs file`, the name of the control shall be edited with a text editor: _...LazarusResources.Add('` Filename`','PNG',[[lazres|..._ (_Filename_ shall be replaced with the name of the control, for example _TCon_).
117129
118130### Using lazres
119131
120-`lazres` is usually located in the Lazarus tools directory.
132+Alternatively, you can use the command line tool `[lazres]]`.
121133
122-You may need to compile [[lazres]] at first use. Simply open the *lazres.lpi* in the IDE and click **run \> build** in the menu.
134+`lazres` is usually located in the Lazarus tools directory. You may need to compile it at first use. Simply open the _lazres.lpi_ in the IDE and click **run > build** in the menu.
123135
124136**Create lrs file**
125137
126-To create the lrs file run:
138+To create the lrs file run:
127139
128140`~/lazarus/tools/lazres samplepackage.lrs TMyCom.png`
129141
130-or, if high-resolution images are available
142+or, if high-resolution images are available
131143
132144`~/lazarus/tools/lazres samplepackage.lrs TMyCom.png TMyCom_150.pgn TMyCom_200.png`
133145
134-Where
146+where
135147
136-- *samplepackage* is the name of your package
137-- *TMyCom* is the name of your component! The name of the image must match the name of your component!
148+ * _samplepackage_ is the name of your package
149+ * _TMyCom_ is the name of your component! The name of the image must match the name of your component!
138150
139-You 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 ...
151+You can add more than one image to the lrs file by listing all file names after the resource name in the `lazres` instruction, e.g. `~/lazarus/tools/lazres samplepackage.lrs TMyCom.png TMyOtherCom.png ...`
140152
141-**Sample**
153+When there are many images to be added it may be more convenient to list all file names in a separate text file, e.g. `images.txt`, one image per line, and to call this text file in the `lazres` command with a preceding '@', rather than the individual file names:
142154
143-Following is a sample of the resulting *samplepackage.lrs* file.
155+`~/lazarus/tools/lazres samplepackage.lrs @images.txt`.
144156
145- LazarusResources.Add('TMyCom','PNG',[
157+**Sample**
158+
159+Following is a sample of the resulting _samplepackage.lrs_ file.
160+```
161+LazarusResources.Add('TMyCom','PNG',[
146162 #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
147163 +#0#0#0#4'gAMA'#0#0#177#143#11#252'a'#5#0#0#0'|IDAT8O'#237#212#209#10#192' '#8
148164 +#5'P'#247#231#251's'#215#138#133#164#166'\'#220#195'`'#209'c'#157'L'#173#131
149165 +#153#169'd4'#168'dP'#137'r_'#235'5'#136'@Zmk'#16'd9'#144#176#232#164'1'#247
150166 +'I'#8#160'IL'#206'C'#179#144#12#199#140'.'#134#244#141'~'#168#247#209'S~;'#29
151167 +'V+'#196#201'^'#10#15#150'?'#255#18#227#206'NZ>42'#181#159#226#144#15'@'#201
152168 +#148#168'e'#224'7f<@4'#130'u_YD'#23#213#131#134'Q]'#158#188#135#0#0#0#0'IEND'
153169 +#174'B`'#130
154170 ]);
171+```
155172
156-Make 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
173+Make 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
174+```
175+initialization
176+ {$I samplepackage.lrs}
177+```
157178
158-``` pascal
179+If the _lrs_ file is in a subfolder(s) it shall be added the following way:
180+```
159181initialization
160- {$I samplepackage.lrs}
182+ {$I subfolder1\subfolder2\samplepackage.lrs}
161183```
162184
163185**Create res file**
164186
165-lazres can also create a compiled resource file, just specify a file with the .res extension, e.g.:
166-
167-`~/lazarus/tools/lazres samplepackage.`**`res`**` TMyCom.png`
187+lazres can also create a compiled resource file, just specify a file with the .res extension, e.g.:
168188
169-or, with high-res icons:
189+`~/lazarus/tools/lazres samplepackage.**res** TMyCom.png`
170190
171-`~/lazarus/tools/lazres samplepackage.`**`res`**` TMyCom.png TMyCom_150.png TMyCom_200.png`
191+or, with high-res icons:
172192
173-In this case, instead of including the lrs file in the initialization section, just include the resource file anywhere in your component source file
193+`~/lazarus/tools/lazres samplepackage.**res** TMyCom.png TMyCom_150.png TMyCom_200.png`
174194
175-``` pascal
176- {$R samplepackage.res}
195+In this case, instead of including the lrs file in the initialization section, just include the resource file anywhere in your component source file
177196```
197+{$R samplepackage.res}
198+```
178199
179200### Using glazres
180201
181-**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.
202+**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.
182203
183204## Recompiling Packages
184205
185-You 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.
206+You 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.
186207
187208## Removing Packages
188209
189-- To remove installed components: on the IDE menu, click Package \> Configure installed packages. The following image shows the Installed Packages tool.
210+ * To remove installed components: on the IDE menu, click Package > Configure installed packages. The following image shows the Installed Packages tool.
190211
191-```text
192-Installed Components
193212```
213+[![Installed Components](/wiki-images/0/0e/How_to_write_lazarus_component_installed_components.png)](</File:How_to_write_lazarus_component_installed_components.png> "Installed Components")
214+```
194215
195-- Select the package you want to uninstall and click Uninstall selection.
216+ * Select the package you want to uninstall and click Uninstall selection.
196217
197-If 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.
218+If 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.
198219
199220## Enhancing mycom.pas
200221
201-- 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.
202-- The *OnChange2* shows how to create events
203-- The *OnSample* shows how to create custom events
204-- *MyText* and *MyText2* shows different ways to write properties.
205-- You can use TComboBox instead of TCustomComboBox as the base class, which publishes all properties as TComboBox.
206-- 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.
222+ * 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.
223+ * The _OnChange2_ shows how to create events
224+ * The _OnSample_ shows how to create custom events
225+ * _MyText_ and _MyText2_ shows different ways to write properties.
226+ * You can use TComboBox instead of TCustomComboBox as the base class, which publishes all properties as TComboBox.
227+ * 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.
207228
208-``` pascal
229+```
209230unit mycom;
210-
211-{$mode objfpc}{$H+}
212-
213-interface
214-
215-uses
216- Classes, SysUtils, StdCtrls, Forms, Dialogs,
217- LCLType,LCLIntf,lresources,LCLProc;
218-
219-type
220-
221- TSampleEvent = procedure(MyText: String) of Object;
222-
223- TMyCom = class (TCustomComboBox)
224- private
225- FMyText: String;
226- FOnChange2: TNotifyEvent;
227- FOnSample: TSampleEvent;
228- public
229- constructor Create(TheOwner: TComponent); override;
230- procedure CreateWnd; override;
231- procedure Change; override;
232- protected
233- function GetMyText2: String;
234- procedure SetMyText2(MyText: String);
235- published
236- property MyText: String read FMyText write FMyText;
237- property MyText2: String read GetMyText2 write SetMyText2;
238- property OnChange2: TNotifyEvent read FOnChange2 write FOnChange2;
239- property OnSample: TSampleEvent read FOnSample write FOnSample;
240231
241- // properties from TComboBox
242- property Align;
243- property Anchors;
244- property ArrowKeysTraverseList;
245- property AutoComplete;
246- property AutoCompleteText;
247- property AutoDropDown;
248- property AutoSelect;
249- property AutoSize;
250- property BidiMode;
251- property BorderSpacing;
252- property CharCase;
253- property Color;
254- property Ctl3D;
255- property Constraints;
256- property DragCursor;
257- property DragMode;
258- property DropDownCount;
259- property Enabled;
260- property Font;
261- property ItemHeight;
262- property ItemIndex;
263- property Items;
264- property ItemWidth;
265- property MaxLength;
266- property OnChange;
267- property OnChangeBounds;
268- property OnClick;
269- property OnCloseUp;
270- property OnContextPopup;
271- property OnDblClick;
272- property OnDragDrop;
273- property OnDragOver;
274- property OnDrawItem;
275- property OnEndDrag;
276- property OnDropDown;
277- property OnEditingDone;
278- property OnEnter;
279- property OnExit;
280- property OnGetItems;
281- property OnKeyDown;
282- property OnKeyPress;
283- property OnKeyUp;
284- property OnMeasureItem;
285- property OnMouseDown;
286- property OnMouseMove;
287- property OnMouseUp;
288- property OnStartDrag;
289- property OnSelect;
290- property OnUTF8KeyPress;
291- property ParentBidiMode;
292- property ParentColor;
293- property ParentCtl3D;
294- property ParentFont;
295- property ParentShowHint;
296- property PopupMenu;
297- property ReadOnly;
298- property ShowHint;
299- property Sorted;
300- property Style;
301- property TabOrder;
302- property TabStop;
303- property Text;
304- property Visible;
305- end;
306-
307-procedure Register;
308-
309-implementation
310-
311-procedure Register;
312-begin
313- RegisterComponents('Sample',[TMyCom]);
314-end;
315-
316-constructor TMyCom.Create(TheOwner: TComponent);
317-begin
318- inherited Create(TheOwner);
319- Self.Style := csDropDownList;
320-end;
321-
322-procedure TMyCom.CreateWnd;
323-begin
324- inherited CreateWnd;
325- Items.Assign(Screen.Fonts);
326-end;
327-
328-procedure TMyCom.Change;
329-begin
330- inherited;
331- if Assigned(FOnChange2) then FOnChange2(Self);
332- if Assigned(FOnSample) then FOnSample(FMyText);
333-end;
334-
335-function TMyCom.GetMyText2: String;
336-begin
337- Result:=FMyText;
338-end;
339-
340-procedure TMyCom.SetMyText2(MyText: String);
341-begin
342- FMyText:=MyText;
343-end;
344-
345-initialization
346- {$I samplepackage.lrs}
347-
348-end.
232+ {$mode objfpc}{$H+}
233+
234+ interface
235+
236+ uses
237+ Classes, SysUtils, StdCtrls, Forms, Dialogs,
238+ LCLType,LCLIntf,lresources,LCLProc;
239+
240+ type
241+
242+ TSampleEvent = procedure(MyText: String) of Object;
243+
244+ TMyCom = class (TCustomComboBox)
245+ private
246+ FMyText: String;
247+ FMyText3: String;
248+ FConcatenatedText : String;
249+ FOnChange2: TNotifyEvent;
250+ FOnSample: TSampleEvent;
251+ procedure LoadedAll ; override; //Or Load - see which one will work
252+ public
253+ constructor Create(TheOwner: TComponent); override;
254+ procedure CreateWnd; override;
255+ procedure Change; override;
256+ protected
257+ function GetMyText2: String;
258+ procedure SetMyText2(MyText: String);
259+ published
260+ property MyText: String read FMyText write FMyText;
261+ property MyText2: String read GetMyText2 write SetMyText2;
262+ property MyText3: String read FMyText3 write FMyText3;
263+ property OnChange2: TNotifyEvent read FOnChange2 write FOnChange2;
264+ property OnSample: TSampleEvent read FOnSample write FOnSample;
265+
266+ // properties from TComboBox
267+ property Align;
268+ property Anchors;
269+ property ArrowKeysTraverseList;
270+ property AutoComplete;
271+ property AutoCompleteText;
272+ property AutoDropDown;
273+ property AutoSelect;
274+ property AutoSize;
275+ property BidiMode;
276+ property BorderSpacing;
277+ property CharCase;
278+ property Color;
279+ property Ctl3D;
280+ property Constraints;
281+ property DragCursor;
282+ property DragMode;
283+ property DropDownCount;
284+ property Enabled;
285+ property Font;
286+ property ItemHeight;
287+ property ItemIndex;
288+ property Items;
289+ property ItemWidth;
290+ property MaxLength;
291+ property OnChange;
292+ property OnChangeBounds;
293+ property OnClick;
294+ property OnCloseUp;
295+ property OnContextPopup;
296+ property OnDblClick;
297+ property OnDragDrop;
298+ property OnDragOver;
299+ property OnDrawItem;
300+ property OnEndDrag;
301+ property OnDropDown;
302+ property OnEditingDone;
303+ property OnEnter;
304+ property OnExit;
305+ property OnGetItems;
306+ property OnKeyDown;
307+ property OnKeyPress;
308+ property OnKeyUp;
309+ property OnMeasureItem;
310+ property OnMouseDown;
311+ property OnMouseMove;
312+ property OnMouseUp;
313+ property OnStartDrag;
314+ property OnSelect;
315+ property OnUTF8KeyPress;
316+ property ParentBidiMode;
317+ property ParentColor;
318+ property ParentCtl3D;
319+ property ParentFont;
320+ property ParentShowHint;
321+ property PopupMenu;
322+ property ReadOnly;
323+ property ShowHint;
324+ property Sorted;
325+ property Style;
326+ property TabOrder;
327+ property TabStop;
328+ property Text;
329+ property Visible;
330+ end;
331+
332+ procedure Register;
333+
334+ implementation
335+
336+ procedure Register;
337+ begin
338+ RegisterComponents('Sample',[TMyCom]);
339+ end;
340+
341+ constructor TMyCom.Create(TheOwner: TComponent);
342+ begin
343+ inherited Create(TheOwner);
344+ Self.Style := csDropDownList;
345+ end;
346+
347+ //If necessary, complete initialisation after settings are loaded from the GUI. Doing it in an earlier stage (for example .Create might cause unpredictable results.)
348+ procedure TSerialSelector.LoadedAll;
349+ begin
350+ inherited;
351+ FConcatenatedText := FMyText + FMyText3;
352+ end;
353+
354+ procedure TMyCom.CreateWnd;
355+ begin
356+ inherited CreateWnd;
357+ Items.Assign(Screen.Fonts);
358+ end;
359+
360+ procedure TMyCom.Change;
361+ begin
362+ inherited;
363+ if Assigned(FOnChange2) then FOnChange2(Self);
364+ if Assigned(FOnSample) then FOnSample(FMyText);
365+ end;
366+
367+ function TMyCom.GetMyText2: String;
368+ begin
369+ Result:=FMyText;
370+ end;
371+
372+ procedure TMyCom.SetMyText2(MyText: String);
373+ begin
374+ FMyText:=MyText;
375+ end;
376+
377+ initialization
378+ {$I samplepackage.lrs}
379+
380+ end.
349381```
350382
351-Also 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:
352-
353-``` pascal
383+Also 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:
384+```
354385Published
355-...
356- property Height: Integer;
357- property Width: Integer;
358-...
386+ ...
387+ property Height: Integer;
388+ property Width: Integer;
389+ ...
359390```
360391
361392## Using embedded (visual) components
362393
363-It's possible to use standard components embedded in your own components (look for example at TLabeledEdit or TButtonPanel).
364-
365-Let'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:
394+It's possible to use standard components embedded in your own components (look for example at TLabeledEdit or TButtonPanel).
366395
367-- Add a private attribute for the label component (FEmbeddedLabel: TLabel;).
368-- Add a published read-only property for the label component (property EmbeddedLabel: TLabel read FEmbeddedLabel;)
369-- Create the label in the component's (overridden) constructor (FEmbeddedLabel := TLabel.Create(self); )
370-- Set the parent of the component (FEmbeddedLabel.Parent := self;)
371-- 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);).
396+Let'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:
372397
373-To sum it up you would get something like this (only the essential parts are shown):
398+ * Add a private attribute for the label component (FEmbeddedLabel: TLabel;).
399+ * Add a published read-only property for the label component (property EmbeddedLabel: TLabel read FEmbeddedLabel;)
400+ * Create the label in the component's (overridden) constructor (FEmbeddedLabel := TLabel.Create(self); )
401+ * Set the parent of the component (FEmbeddedLabel.Parent := self;)
402+ * 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);).
374403
375-``` pascal
404+To sum it up you would get something like this (only the essential parts are shown):
405+```
376406TEnhancedPanel = class(TCustomControl)
377-private
378- { The new attribute for the embedded label }
379- FEmbeddedLabel: TLabel;
380-
381-public
382- { The constructor must be overriden so the label can be created }
383- constructor Create(AOwner: TComponent); override;
384-
385-published
386- { Make the label visible in the IDE }
387- property EmbeddedLabel: TLabel read FEmbeddedLabel;
388-end;
389-
390-implementation
391-
392-constructor TEnhancedPanel.Create(AOwner: TComponent);
393-begin
394- inherited Create(AOwner);
395-
396- // Set default width and height
397- with GetControlClassDefaultSize do
398- SetInitialBounds(0, 0, CX, CY);
399-
400- // Add the embedded label
401- FEmbeddedLabel := TLabel.Create(Self); // Add the embedded label
402- FEmbeddedLabel.Parent := self; // Show the label in the panel
403- FEmbeddedLabel.SetSubComponent(true); // Tell the IDE to store the modified properties
404- FLabel.Name := 'EmbeddedLabel';
405- FLabel.Caption := 'Howdy World!';
406-
407- // Make sure the embedded label can not be selected/deleted within the IDE
408- FLabel.ControlStyle := FLabel.ControlStyle - [csNoDesignSelectable];
409-
410- // Set other properties if necessary
411- //...
412-
413-end;
407+ private
408+ { The new attribute for the embedded label }
409+ FEmbeddedLabel: TLabel;
410+
411+ public
412+ { The constructor must be overriden so the label can be created }
413+ constructor Create(AOwner: TComponent); override;
414+
415+ published
416+ { Make the label visible in the IDE }
417+ property EmbeddedLabel: TLabel read FEmbeddedLabel;
418+ end;
419+
420+ implementation
421+
422+ constructor TEnhancedPanel.Create(AOwner: TComponent);
423+ begin
424+ inherited Create(AOwner);
425+
426+ // Set default width and height
427+ with GetControlClassDefaultSize do
428+ SetInitialBounds(0, 0, CX, CY);
429+
430+ // Add the embedded label
431+ FEmbeddedLabel := TLabel.Create(Self); // Add the embedded label
432+ FEmbeddedLabel.Parent := self; // Show the label in the panel
433+ FEmbeddedLabel.SetSubComponent(true); // Tell the IDE to store the modified properties
434+ FLabel.Name := 'EmbeddedLabel';
435+ FLabel.Caption := 'Howdy World!';
436+
437+ // Make sure the embedded label can not be selected/deleted within the IDE
438+ FLabel.ControlStyle := FLabel.ControlStyle - [csNoDesignSelectable];
439+
440+ // Set other properties if necessary
441+ //...
442+
443+ end;
414444```
415445
416-The 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)
446+The 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>)
417447
418448## Using custom paint procedure
419449
420-You can always subclass a component inside your program. For example, this implements a custom Paint procedure to a TLabel:
421-
422-``` pascal
450+You can always subclass a component inside your program. For example, this implements a custom Paint procedure to a TLabel:
451+```
423452type
424- TMyLabel = class(TLabel)
425- protected
426- procedure Paint; override;
427- end;
428-{...}
429-implementation
430-{...}
431-procedure TMyLabel.Paint;
432-begin
433- // your code to implement Paint, for example
434- Canvas.TextOut(0,0,Caption);
435-end;
453+ TMyLabel = class(TLabel)
454+ protected
455+ procedure Paint; override;
456+ end;
457+ {...}
458+ implementation
459+ {...}
460+ procedure TMyLabel.Paint;
461+ begin
462+ // your code to implement Paint, for example
463+ Canvas.TextOut(0,0,Caption);
464+ end;
436465```
437466
438-Now you can create a MyLabel inside your program, at run time, with that overridden Paint procedure instead of the standard one.
439-
440-For most components, and for most methods, it would be recommendable to call inherited procedure inside it:
467+Now you can create a MyLabel inside your program, at run time, with that overridden Paint procedure instead of the standard one.
441468
442-``` pascal
469+For most components, and for most methods, it would be recommendable to call inherited procedure inside it:
470+```
443471procedure TMyLabel.Paint;
444-begin
445-
446- inherited Paint; /////////////////////
447-
448- // your code to implement Paint, for example
449- Canvas.TextOut(0,0,Caption);
450-end;
472+ begin
473+
474+ inherited Paint; /////////////////////
475+
476+ // your code to implement Paint, for example
477+ Canvas.TextOut(0,0,Caption);
478+ end;
451479```
452480
453-However, inherited behavior is not desirable in this case, since the second writing action would overlap the first (inherited) one.
481+However, inherited behavior is not desirable in this case, since the second writing action would overlap the first (inherited) one.
454482
455483## Integrating the component with the IDE
456484
457485### Property editors
458486
459-Property 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.
460-
461-Each 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.
487+Property 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.
462488
463-``` pascal
464- TPropertyEditor = class
465- public
466- function AllEqual: Boolean; Virtual;
467- function AutoFill: Boolean; Virtual;
468- procedure Edit; Virtual; // Activated by double-clicking the property value
469- procedure ShowValue; Virtual; // Activated by control-clicking the property value
470- function GetAttributes: TPropertyAttributes; Virtual;
471- function GetEditLimit: Integer; Virtual;
472- function GetName: ShortString; Virtual;
473- procedure GetProperties(Proc: TGetPropEditProc); Virtual;
474- function GetHint(HintType: TPropEditHint; x, y: integer): String; Virtual;
475- function GetDefaultValue: AnsiString; Virtual;
476- procedure GetValues(Proc: TGetStrProc); Virtual;
477- procedure SetValue(const NewValue: AnsiString); Virtual;
478- procedure UpdateSubProperties; Virtual;
479- function SubPropertiesNeedsUpdate: Boolean; Virtual;
480- function IsDefaultValue: Boolean; Virtual;
481- function IsNotDefaultValue: Boolean; Virtual;
482- // ... shortened
483- end;
489+Each 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.
484490```
485-
486-A good example for a property editor is the TFont property editor.
491+TPropertyEditor = class
492+ public
493+ function AllEqual: Boolean; Virtual;
494+ function AutoFill: Boolean; Virtual;
495+ procedure Edit; Virtual; // Activated by double-clicking the property value
496+ procedure ShowValue; Virtual; // Activated by control-clicking the property value
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+ // ... shortened
510+ end;
511+```
487512
488-One 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.
513+A good example for a property editor is the TFont property editor.
489514
490-For 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:
515+One 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.
491516
492-``` pascal
517+For 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:
518+```
493519TClassPropertyEditor = class(TPropertyEditor)
494-public
495- constructor Create(Hook: TPropertyEditorHook; APropCount: Integer); Override;
496- function GetAttributes: TPropertyAttributes; Override;
497- procedure GetProperties(Proc: TGetPropEditProc); Override;
498- function GetValue: AnsiString; Override;
499- property SubPropsTypeFilter: TTypeKinds Read FSubPropsTypeFilter
500- Write SetSubPropsTypeFilter
501- Default tkAny;
502-end;
520+ public
521+ constructor Create(Hook: TPropertyEditorHook; APropCount: Integer); Override;
522+ function GetAttributes: TPropertyAttributes; Override;
523+ procedure GetProperties(Proc: TGetPropEditProc); Override;
524+ function GetValue: AnsiString; Override;
525+ property SubPropsTypeFilter: TTypeKinds Read FSubPropsTypeFilter
526+ Write SetSubPropsTypeFilter
527+ Default tkAny;
528+ end;
503529```
504530
505-Going 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:
531+Going 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:
532+```
533+TFontPropertyEditor = class(TClassPropertyEditor)
534+ public
535+ procedure Edit; Override;
536+ function GetAttributes: TPropertyAttributes; Override;
537+ end;
538+
539+ procedure TFontPropertyEditor.Edit;
540+ var
541+ FontDialog: TFontDialog;
542+ begin
543+ FontDialog := TFontDialog.Create(NIL);
544+ try
545+ FontDialog.Font := TFont(GetObjectValue(TFont));
546+ FontDialog.Options := FontDialog.Options + [fdShowHelp, fdForceFontExist];
547+ if FontDialog.Execute then SetPtrValue(FontDialog.Font);
548+ finally
549+ FontDialog.Free;
550+ end;
551+ end;
552+
553+ function TFontPropertyEditor.GetAttributes: TPropertyAttributes;
554+ begin
555+ Result := [paMultiSelect, paSubProperties, paDialog, paReadOnly];
556+ end;
557+```
506558
507-``` pascal
508- TFontPropertyEditor = class(TClassPropertyEditor)
509- public
510- procedure Edit; Override;
511- function GetAttributes: TPropertyAttributes; Override;
512- end;
559+The 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>)
513560
514-procedure TFontPropertyEditor.Edit;
515-var
516- FontDialog: TFontDialog;
517-begin
518- FontDialog := TFontDialog.Create(NIL);
519- try
520- FontDialog.Font := TFont(GetObjectValue(TFont));
521- FontDialog.Options := FontDialog.Options + [fdShowHelp, fdForceFontExist];
522- if FontDialog.Execute then SetPtrValue(FontDialog.Font);
523- finally
524- FontDialog.Free;
525- end;
526-end;
561+### Component editors
527562
528-function TFontPropertyEditor.GetAttributes: TPropertyAttributes;
529-begin
530- Result := [paMultiSelect, paSubProperties, paDialog, paReadOnly];
531-end;
532-```
563+Component editors control the behavior of right-clicking and double clicking components in the form designer.
533564
534-The 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)
565+Each 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:
566+```
567+TBaseComponentEditor = class
568+ protected
569+ public
570+ constructor Create(AComponent: TComponent;
571+ ADesigner: TComponentEditorDesigner); Virtual;
572+ procedure Edit; Virtual; Abstract;
573+ procedure ExecuteVerb(Index: Integer); Virtual; Abstract;
574+ function GetVerb(Index: Integer): String; Virtual; Abstract;
575+ function GetVerbCount: Integer; Virtual; Abstract;
576+ procedure PrepareItem(Index: Integer; const AnItem: TMenuItem); Virtual; Abstract;
577+ procedure Copy; Virtual; Abstract;
578+ function IsInInlined: Boolean; Virtual; Abstract;
579+ function GetComponent: TComponent; Virtual; Abstract;
580+ function GetDesigner: TComponentEditorDesigner; Virtual; Abstract;
581+ function GetHook(out Hook: TPropertyEditorHook): Boolean; Virtual; Abstract;
582+ procedure Modified; Virtual; Abstract;
583+ end;
584+```
535585
536-### Component editors
586+The 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.
537587
538-Component editors control the behavior of right-clicking and double clicking components in the form designer.
588+Other important methods from TBaseComponentEditor are:
539589
540-Each 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:
590+ * **ExecuteVerb(Index)** \- Executes one of the extra menu items placed on the right-click popup menu;
591+ * **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;
592+ * **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;
593+ * **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;
594+ * **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.
541595
542-``` pascal
543- TBaseComponentEditor = class
544- protected
545- public
546- constructor Create(AComponent: TComponent;
547- ADesigner: TComponentEditorDesigner); Virtual;
548- procedure Edit; Virtual; Abstract;
549- procedure ExecuteVerb(Index: Integer); Virtual; Abstract;
550- function GetVerb(Index: Integer): String; Virtual; Abstract;
551- function GetVerbCount: Integer; Virtual; Abstract;
552- procedure PrepareItem(Index: Integer; const AnItem: TMenuItem); Virtual; Abstract;
553- procedure Copy; Virtual; Abstract;
554- function IsInInlined: Boolean; Virtual; Abstract;
555- function GetComponent: TComponent; Virtual; Abstract;
556- function GetDesigner: TComponentEditorDesigner; Virtual; Abstract;
557- function GetHook(out Hook: TPropertyEditorHook): Boolean; Virtual; Abstract;
558- procedure Modified; Virtual; Abstract;
559- end;
596+A 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:
560597```
598+TCheckListBoxComponentEditor = class(TComponentEditor)
599+ protected
600+ procedure DoShowEditor;
601+ public
602+ procedure ExecuteVerb(Index: Integer); override;
603+ function GetVerb(Index: Integer): String; override;
604+ function GetVerbCount: Integer; override;
605+ end;
606+
607+ procedure TCheckGroupComponentEditor.DoShowEditor;
608+ var
609+ Dlg: TCheckGroupEditorDlg;
610+ begin
611+ Dlg := TCheckGroupEditorDlg.Create(NIL);
612+ try
613+ // .. shortened
614+ Dlg.ShowModal;
615+ // .. shortened
616+ finally
617+ Dlg.Free;
618+ end;
619+ end;
620+
621+ procedure TCheckGroupComponentEditor.ExecuteVerb(Index: Integer);
622+ begin
623+ case Index of
624+ 0: DoShowEditor;
625+ end;
626+ end;
627+
628+ function TCheckGroupComponentEditor.GetVerb(Index: Integer): String;
629+ begin
630+ Result := 'CheckBox Editor...';
631+ end;
632+
633+ function TCheckGroupComponentEditor.GetVerbCount: Integer;
634+ begin
635+ Result := 1;
636+ end;
637+```
561638
562-The 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.
639+## Design-time component debugging
563640
564-Other important methods from TBaseComponentEditor are:
641+To catch design-time-errors in a (newly created) _dbgcomponent_ :
565642
566-- **ExecuteVerb(Index)** - Executes one of the extra menu items placed on the right-click popup menu;
567-- **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;
568-- **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;
569-- **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;
570-- **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.
643+ * open project `C:\lazarus\ide\lazarus.lpi`;
644+ * run project;
645+ * set breakpoint in _dbgcomponent_ in main (1st) app;
646+ * use _dbgcomponent_ pascal code in second app;
647+ * step through design-time component code in debug-session; do whatever is necessary;
571648
572-A 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:
649+## Adding contensts for the _Information box_
573650
574-``` pascal
575- TCheckListBoxComponentEditor = class(TComponentEditor)
576- protected
577- procedure DoShowEditor;
578- public
579- procedure ExecuteVerb(Index: Integer); override;
580- function GetVerb(Index: Integer): String; override;
581- function GetVerbCount: Integer; override;
582- end;
651+The Lazarus IDE has an Information Box, where custom information about the published properties of the controls is shown.
583652
584-procedure TCheckGroupComponentEditor.DoShowEditor;
585-var
586- Dlg: TCheckGroupEditorDlg;
587-begin
588- Dlg := TCheckGroupEditorDlg.Create(NIL);
589- try
590- // .. shortened
591- Dlg.ShowModal;
592- // .. shortened
593- finally
594- Dlg.Free;
595- end;
596-end;
653+
654+[![Lazarus Information Box](/wiki-images/4/40/Lazarus_Information_Box.png)](</File:Lazarus_Information_Box.png> "Lazarus Information Box")
597655
598-procedure TCheckGroupComponentEditor.ExecuteVerb(Index: Integer);
599-begin
600- case Index of
601- 0: DoShowEditor;
602- end;
603-end;
656+There are two ways to add such info:
604657
605-function TCheckGroupComponentEditor.GetVerb(Index: Integer): String;
606-begin
607- Result := 'CheckBox Editor...';
608-end;
658+### Simple
609659
610-function TCheckGroupComponentEditor.GetVerbCount: Integer;
611-begin
612- Result := 1;
613-end;
660+Type a comment immediately before the definition of the published property (Note: This does not work in some versions of Lazarus around 4.0).
661+
662+For example:
614663```
664+published
665+ ...
666+ //Enables or disables the serial connection
667+ property Active: boolean read FActive write SetActive;
668+```
615669
616-## Design-time component debugging
670+### FPDoc Editor
617671
618-To catch design-time-errors in a (newly created) *dbgcomponent*:
672+[![Lazarus FPDoc Editor](/wiki-images/2/28/Lazarus_FPDoc_Editor.png)](</File:Lazarus_FPDoc_Editor.png> "Lazarus FPDoc Editor")
619673
620-- open project `C:\lazarus\ide\lazarus.lpi`;
621-- run project;
622-- set breakpoint in *dbgcomponent* in main (1st) app;
623-- use *dbgcomponent* pascal code in second app;
624-- step through design-time component code in debug-session; do whatever is necessary;
674+1\. Open _Main Menu — View — FPDoc Editor_
625675
676+2\. Select a published property.
677+
678+3\. Click “Create help item”
679+
680+4\. A dialogue for selecting the folder of the _.xml_ file with the help items is open. Select (create if necessary) a subfolder in the main folder of the _.lpk_.
681+
682+The dialogue for the folder will not be shown after the selection is done. If necessary, the folder can be changed later from the _Package editor dialogue – Options — IDE integration — FPDoc settings — Search paths:_
683+
684+5\. Enter the desired text in the _FPDoc Editor_.
685+
686+6\. You can apply some formatting with the buttons on the left. The text with _Remark_ formatting is not displayed.
687+
688+7\. When done, rebuild the Lazarus IDE with the updated package.
689+
690+8\. Hints: • Keeping the _FPDoc Editor_ open might cause some problems if version control is used. If this is your case, do not keep it open when it is not needed.
691+
626692## See also
627693
628-- [[Extending the IDE]]
629-- [[Lazarus Packages]] - A guide for creating a package under Lazarus
630-- [[Components and Code examples]]
631-- [[Lazarus Components]]
632-- [[IDE Window: Add to Package]]
633-- [[IDE Window: Create new package component]]
694+ * [[Extending the IDE]]
695+ * [[Lazarus Packages]] \- A guide for creating a package under Lazarus
696+ * [[Components and Code examples]]
697+ * [[Lazarus Components]]
698+ * [[IDE Window: Add to Package]]
699+ * [[IDE Window: Create new package component]]
634700
635-You can post questions regarding this page [here](http://forum.lazarus.freepascal.org/index.php/topic,5789.0.html)
701+You can post questions regarding this page [here](<http://forum.lazarus.freepascal.org/index.php/topic,5789.0.html>)
636702
637703---
638-*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).*
639-Category: Code
640-Category: Component Creation
641-Category: Lazarus
642-Category: Packages
643-Category: Tutorials
704+*From the Free Pascal wiki, page "How To Write Lazarus Component", as the Internet Archive kept it on 2026-03-11, last changed on 2025-10-22. 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