Sign in Join
Wiki copytemporary

Lazarus Nongraphical Visual Component Example Code: what changed

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

OldNewLine
11### Introduction
22
33There are many property types in Lazarus, and when writing a descendent of TComponent it is not always straightforward how to implement some of them. After searching many sources I have written a component template that includes most property types.
44
55The code below is a working component that could be packaged and compiled in the IDE, but its purpose is to serve as a source of copy/paste snippets.
66
77Here's what it looks like in the Lazarus Object Inspector:
88
9-<figure>
10-<img src="myvisualcomponent_properties11.png" title="myvisualcomponent_properties11.png" />
11-<figcaption>myvisualcomponent_properties11.png</figcaption>
12-</figure>
9+![](/wiki-images/3/3f/Myvisualcomponent_properties11.png)
1310
1411### Code to copy and paste
1512
1613``` pascal
1714unit MyCustomControl;
1815
1916{
2017= Example of a generic non-graphical component for Lazarus 1.x
2118= - example code for various types of properties and events to show and act correctly in the Object Inspector
2219= - including a custom 'About' dialog property editor
2320=
2421= Although this unit will compile as written, the purpose is to provide template code
2522= for various property types and events that can be copy/pasted into your own component
2623=
2724= Example Property types:
2825= 1) Bitmap
2926= 2) Font
3027= 3) Icon
3128= 4) Stringlist
3229= 5) Options expandable true/false list
3330= 6) User type drop-down list
3431= 7) Simple String
3532= 8) String with pre and post-processing
3633= 9) String Array with indexed properties
3734= 10) String with default value
3835= 11) Integer with default value
3936= 12) Overridden (custom) Tag property
4037= 13) Inherited drop-down list of types
4138= 14) Custom dialog
4239= 15) Custom events
4340= 16) Property hidden from the Object Inspector
4441= 17) Filename property with custom properties for the OpenDialog
4542=
4643= Author: minesadorada@charcodelvalle.com
4744= Date: May 2014
4845= License: LGPL
4946=
5047}
5148{$mode objfpc}{$H+}
5249
5350interface
5451
5552uses
5653 Classes, SysUtils, Forms, Controls, Graphics, Buttons,
5754 LCLIntf, LCLType, LResources,
5855 ExtCtrls, StdCtrls, Dialogs, PropEdits;
5956
6057const
6158 C_VERSION = '1.1'; // Remember to co-ordinate with the Package manager VersionInfo
6259
6360 C_ERRORMESSAGE = '%s error:' + LineEnding + '%s';
6461// Used with Exception.CreateFmt
6562
6663type
6764 // Drop-down list in Object Inspector
6865 tmcType = (mcType1, mcType2, mcType3);
6966
7067 // Expandable list of Options in Object Inspector
7168 tOptions = (Opt1, Opt2, Opt3);
7269 tOptionsFlags = set of tOptions;
7370
7471 tAboutString = string; // Unique string type used in 'About' property editor
7572 tFilenameString = string; // Unique string type used in MyFilename property editor
7673
7774 TSampleEvent = procedure(MyText: string) of object; // Custom Event type
7875
7976 TMyVisualComponent = class(TComponent) // Non-graphical ancestor
8077 private
8178 { Private declarations }
8279 fAboutString: string; // Dummy string for 'About' property
8380 fFileNameString: tFilenameString; // Filename
8481 fmcType: tmcType; // Type defined above
8582 fSimpleString: string; // Direct read and write (no methods)
8683 fProcessedString: string; // Methods used to read and write the property
8784 fStringWithDefault: string; // Object Inspector shows default
8885 fIntegerWithDefault: integer; // Object Inspector shows default
8986 fTag: string; // Property overrides normal Tag property
9087 fOptions: tOptionsFlags; // Variable of Set type. * If Opt1 IN fOptions then...
9188 fIcon: TIcon; // Assigned to Application Icon by default
9289 fFont: TFont; // Assigned only if MyFont property set
9390 fBitMap: TBitMap; // Assigned only if MyBitmap property set
9491 fSizeConstraints: TSizeConstraintsOptions;
9592 // Built-in set (part of TSizeConstraints object)
9693 fStringArray: array[0..3] of string;
9794 // Stores values of String1,String2,String3 and String4 properties
9895 fStringList: TStrings; // Holds MyStringList property values
9996 fVersion: string; // Holds read-only version property
10097 FOnSample: TSampleEvent; // Custom event
10198
10299 fHiddenString: string; // Hidden property
103100 fOnChangeHiddenString: TNotifyEvent; // Custom event
104101
105102 { Private methods }
106103
107104 procedure SetProcessedString(AValue: string);
108105 function GetProcessedString: string;
109106 // Read and Write procedures for property ProcessedString
110107
111108 procedure SetTag(AValue: string); // Write procedure for overridden property 'Tag'
112109
113110 function GetStringValue(const AIndex: integer): string;
114111 // Indexed Get Method
115112 procedure SetStringValue(const AIndex: integer; AValue: string);
116113 // Indexed Set method
117114
118115 procedure SetStrings(const AValue: TStrings);
119116 // Needed to use a TStrings property. FStringList also needs to be created in the Constructor
120117
121118 procedure SetFont(const AValue: TFont);
122119 // Needed to use a Font property. fFont also needs to be created in the Constructor
123120
124121 procedure SetBitMap(const AValue: TBitmap);
125122 // Needed to use a BitMap property. fBitMap also needs to be created in the Constructor
126123
127124 // Sets the HiddenString property
128125 procedure SetHiddenString(const AValue: string);
129126 protected
130127 { Protected declarations }
131128 // Can be used if you plan to subclass this component
132129 public
133130 { Public declarations }
134131 constructor Create(AOwner: TComponent); override; // Constructor must be public
135132 destructor Destroy; override; // Destructor must be public
136133
137134 { Public properties }
138135 // This section used for properties you don't want to display in the Object Inspector (or array properties)
139136 property HiddenString: string read fHiddenString write SetHiddenString;
140137 // Public property hidden from the Object Inspector. * SetHiddenString method can trigger custom event OnChangeHiddenString
141138 published
142139 {
143140 Published declarations (Displayed in the Object Inspector)
144141 Note: Comments immediately above the property declarations are displayed in the Lazarus Object Inspector lower pane
145142 }
146143
147144 // The custom property editor TAboutPropertyEditor is used in the Object Inspector
148145 property About: tAboutString read fAboutString; // Example of custom 'About' dialog
149146
150147 // Example of drop-down list in the Object Inspector
151148 // Default is displayed in different font and is set in Constructor
152149 property MyTypeList: tmcType read fmcType write fmcType default mcType2;
153150
154151 // Direct access read + write
155152 property SimpleString: string read fSimpleString write fSimpleString;
156153
157154 // Example of using Read and Write methods
158155 property ProcessedString: string read GetProcessedString write SetProcessedString;
159156
160157 // Example of pseudo-default property of a string (value is set in Constructor)
161158 property StringWithDefault: string read fStringWithDefault write fStringWithDefault;
162159
163160 // Example of an expandable list of booleans in the Object Inspector
164161 property Options: tOptionsFlags read fOptions write fOptions;
165162
166163 // Browse for Icon dialog in the Object Inspector. * No need to Create fIcon in constructor
167164 property MyIcon: TIcon read fIcon write fIcon;
168165
169166 // Browse for Font dialog in the Object Inspector. * fFont needs to be created in Constructor
170167 property MyFont: TFont read fFont write SetFont; // * Note the SetFont method
171168
172169 // * Note the SetBitmap method
173170 // Browse for Bitmap dialog in the Object Inspector. * fBitmap needs to be created in Constructor
174171 property MyBitMap: TBitmap read fBitMap write SetBitmap;
175172
176173 // Showing a built-in list of booleans in the Object Inspector
177174 property MySizeConstraints: TSizeConstraintsOptions
178175 read fSizeConstraints write fSizeConstraints;
179176
180177 // Default is displayed in different font but has to be set to the value 2 in Constructor
181178 property IntegerWithDefault: integer read fIntegerWithDefault
182179 write fIntegerWithDefault default 2; // Example of integer default
183180
184181 // The method is passed the index as a parameter
185182 // Example of using an indexed read and write method to store strings in an array
186183 property String1: string index 1 read GetStringValue write SetStringValue;
187184 // Example of using an indexed read and write method to store strings in an array
188185 property String2: string index 2 read GetStringValue write SetStringValue;
189186 // Example of using an indexed read and write method to store strings in an array
190187 property String3: string index 3 read GetStringValue write SetStringValue;
191188 // Example of using an indexed read and write method to store strings in an array
192189 property String4: string index 4 read GetStringValue write SetStringValue;
193190
194191 // Dialog shows in the Object Inspector. * fStringlist must be Created in the constructor. * Note the SetStrings override
195192 // String list example.
196193 property MyStringList: TStrings read fStringList write SetStrings;
197194
198195 // Example of a file open dialog property
199196 property MyFilename: tFilenameString read fFilenameString write fFilenameString;
200197
201198 // Tag property will now only accept a single alpha character
202199 // Example of overriding an ancestor (TComponent's) Tag property
203200 property Tag: string read fTag write SetTag;
204201
205202 // No need to specify type, read method or write method
206203 // Example of publishing a public property from an ancestor (TComponent)
207204 property ComponentState;
208205
209206 // ReadOnly property
210207 property Version: string read fVersion;
211208
212209 // Example of Custom Event that does nothing
213210 property OnSample: TSampleEvent read FOnSample write FOnSample;
214211
215212 // This custom event is always triggered by a change in the value of HiddenString property
216213 property OnChangeHiddenString: TNotifyEvent
217214 read fOnChangeHiddenString write fOnChangeHiddenString;
218215 end;
219216 // Declaration for the 'About' property editor
220217 TAboutPropertyEditor = class(TClassPropertyEditor)
221218 public
222219 procedure Edit; override;
223220 function GetAttributes: TPropertyAttributes; override;
224221 function GetValue: string; override;
225222 end;
226223 // Declaration for 'MyFilename' property editor
227224 // (See PropEdits unit for a list of property editors to modify)
228225 TMyFileNamePropertyEditor = class(TFileNamePropertyEditor)
229226 public
230227 // Override the Edit method for total control
231228 function GetFilter: string; override;
232229 function GetDialogOptions: TOpenOptions; override;
233230 function GetDialogTitle: string; override;
234231 end;
235232
236233procedure Register; // Must be declared here just before implementation
237234
238235implementation
239236
240237procedure Register;
241238begin
242239 {$I myvisualcomponent_icon.lrs}
243240 // Register this component into the IDE on the 'Additional' component palette
244241 RegisterComponents('Additional', [TMyVisualComponent]);
245242
246243 // Register the custom property editor for the 'About' property
247244 RegisterPropertyEditor(TypeInfo(tAboutString),
248245 TMyVisualComponent, 'About', TAboutPropertyEditor);
249246 // Register the custom property editor for the 'MyFilename' property
250247 RegisterPropertyEditor(TypeInfo(tFilenameString),
251248 TMyVisualComponent, 'MyFilename', TMyFileNamePropertyEditor);
252249
253250 // Note the TypeInfo parameter is made into a unique string type
254251 // (defined earlier in this unit)
255252 // so that the regular property editors in other components don't get confused
256253 // You could simply use TypeInfo(String)
257254end;
258255
259256constructor TMyVisualComponent.Create(AOwner: TComponent);
260257 // Called when form is loaded into the IDE
261258begin
262259 inherited Create(AOwner);
263260 // Initialisation goes here
264261 // Set any properties to their defaults and intialise objects
265262 fmcType := mcType2; // because this was the default value specified
266263 fStringWithDefault := 'Default String'; // Unspecified default
267264 fIntegerWithDefault := 2; // Default value specified
268265 fTag := 'A'; // Unspecified default
269266 fIcon := Application.Icon; // Unspecified default
270267 fFont := TFont.Create; // Needs to be created here for Object Inspector to show options
271268 fBitmap := TBitMap.Create;
272269 // Needs to be created here for Object Inspector to show the bitmap dialog
273270 fStringList := TStringList.Create;
274271 // Needs to be created here for Object Inspector to show dialog
275272 fVersion := C_VERSION;
276273 // Assign read-only property
277274end;
278275
279276destructor TMyVisualComponent.Destroy;
280277begin
281278 // Clean-up code goes here
282279 // FreeandNil any user-created objects here
283280 FreeAndNil(fBitmap);
284281 FreeAndNil(fStringList);
285282 FreeAndNil(fFont);
286283 inherited Destroy;
287284end;
288285
289286// == START PROPERTY EDITOR CODE ==
290287procedure TAboutPropertyEditor.Edit;
291288// Shows a dialog when About property is double-clicked
292289var
293290 tAboutForm: TForm;
294291 OKbutton: TBitBtn;
295292 lbl_Description: TLabel;
296293 sz: string;
297294begin
298295 // Make up message string
299296 sz := 'My component for Lazarus' + LineEnding + 'by email@domain.com' +
300297 LineEnding + LineEnding;
301298 sz += 'Methods:' + LineEnding;
302299 sz += 'MyVisualComponent.Method1' + LineEnding;
303300 sz += 'MyVisualComponent.Method2' + LineEnding;
304301 sz += LineEnding + 'Version: ' + C_VERSION + LineEnding;
305302 sz += 'License: LGPL';
306303 // Create a new dialog
307304 tAboutForm := TForm.CreateNew(nil);
308305 try //.. finally FreeAndNil everything
309306 with tAboutForm do
310307 begin
311308 // Set Form properties
312309 position := poScreenCenter;
313310 borderstyle := bsToolWindow;
314311 Caption := 'About My Component';
315312 formstyle := fsSystemStayOnTop;
316313 color := clSkyBlue;
317314 Height := 240;
318315 Width := 320;
319316 // Create a BitBtn button
320317 okbutton := TBitBtn.Create(tAboutForm);
321318 // Set BitBtn properties
322319 okbutton.Kind := bkClose;
323320 okbutton.left := (Width div 2) - okbutton.Width div 2;
324321 okbutton.top := Height - okbutton.Height - 10;
325322 okbutton.parent := tAboutForm;
326323 // Create a label control
327324 lbl_Description := Tlabel.Create(tAboutForm);
328325 // Set label properties
329326 lbl_Description.left := 8;
330327 lbl_Description.Top := 30;
331328 lbl_Description.Width := 304;
332329 lbl_Description.Height := 200;
333330 lbl_Description.Autosize := False;
334331 lbl_Description.Alignment := taCenter;
335332 lbl_Description.Caption := sz;
336333 lbl_Description.parent := tAboutForm;
337334 // Display the dialog modally
338335 ShowModal;
339336 end;
340337 finally
341338 // Free all resources
342339 FreeAndNil(lbl_Description);
343340 FreeAndNil(okbutton);
344341 FreeAndNil(tAboutForm);
345342 end;
346343end;
347344
348345function TAboutPropertyEditor.GetAttributes: TPropertyAttributes;
349346 // Show the ellipsis
350347begin
351348 Result := [paDialog];
352349end;
353350
354351function TAboutPropertyEditor.GetValue: string;
355352 // Override standard string read method
356353begin
357354 Result := '(Double-click)';
358355end;
359356
360357function TMyFileNamePropertyEditor.GetFilter: string;
361358begin
362359 Result := 'All Files|*.*|Bitmaps|*.bmp|JPegs|*.jpg';
363360end;
364361
365362function TMyFileNamePropertyEditor.GetDialogOptions: TOpenOptions;
366363begin
367364 // To see the full list, drop an OpenDialog onto a form and see the Options property
368365 Result := [ofFileMustExist, ofPathMustExist];
369366end;
370367
371368function TMyFileNamePropertyEditor.GetDialogTitle: string;
372369begin
373370 Result := 'My Custom Title';
374371end;
375372// == END PROPERTY EDITOR CODE ==
376373
377374// == PROPERTY GET/SETS ==
378375procedure TMyVisualComponent.SetProcessedString(AValue: string);
379376begin
380377 // Check to see if a change is necessary
381378 // Avalue can be amended before it is assigned to fProcessedString
382379 if (fProcessedString <> AValue) then
383380 fProcessedString := UpperCase(AValue);
384381end;
385382
386383function TMyVisualComponent.GetProcessedString: string;
387384begin
388385 // Do any error-checking or processing of fProcessedString here.
389386 Result := fProcessedString;
390387end;
391388
392389procedure TMyVisualComponent.SetTag(AValue: string);
393390// Overridden Tag property will only accept letters
394391// If changed via Object Inspector to a non-alpha string, then show custom error message
395392begin
396393 if (fTag <> AValue) and (Length(AValue) > 0) then
397394 if AValue[1] in ['A'..'Z'] + ['a'..'z'] then
398395 fTag := AValue[1]
399396 else
400397 if ComponentState = [csDesigning] then
401398 raise Exception.CreateFmt(C_ERRORMESSAGE,
402399 [Name, 'You can only set the tag property to an alpha value']);
403400 // 'Name' is the Name property of this TComponent instance
404401end;
405402
406403function TMyVisualComponent.GetStringValue(const AIndex: integer): string;
407404begin
408405 // Example of an indexed property Get method
409406 // Retrieve from private string Array
410407 Result := fStringArray[AIndex - 1];
411408end;
412409
413410procedure TMyVisualComponent.SetStringValue(const AIndex: integer; AValue: string);
414411// Example of an indexed property Set method
415412// Value is stored in private string array
416413begin
417414 if (fStringArray[AIndex - 1] <> AValue) then
418415 fStringArray[AIndex - 1] := AValue;
419416end;
420417
421418procedure TMyVisualComponent.SetStrings(const AValue: TStrings);
422419begin
423420 // this is correct statement
424421 FStringList.Assign(AValue);
425422 // this is not correct
426423 // FStrings := AValue;
427424end;
428425
429426procedure TMyVisualComponent.SetFont(const AValue: TFont);
430427begin
431428 // this is correct statement
432429 fFont.Assign(AValue);
433430 // this is not correct
434431 // fFont := AValue;
435432end;
436433
437434procedure TMyVisualComponent.SetBitMap(const AValue: TBitmap);
438435begin
439436 // this is correct statement
440437 fBitmap.Assign(AValue);
441438 // this is not correct
442439 // fBitmap := AValue;
443440end;
444441
445442procedure TMyVisualComponent.SetHiddenString(const AValue: string);
446443// If value is changed then triggers the custom events OnChangeHiddenString and OnSample
447444begin
448445 if (fHiddenString <> AValue) then
449446 begin
450447 fHiddenString := AValue;
451448 // Trigger custom events
452449 if Assigned(fOnChangeHiddenString) then
453450 OnChangeHiddenString(Self);
454451 if Assigned(fOnSample) then
455452 OnSample(AValue);
456453 end;
457454end;
458455// Sample application code that uses the Custom Events as coded
459456// Both are triggered by changing the HiddenString property in SetHiddenString
460457{
461458procedure TForm1.MyVisualComponent1ChangeHiddenString(Sender: TObject);
462459begin
463460 ShowMessage('Hidden String was changed to something new');
464461end;
465462
466463procedure TForm1.MyVisualComponent1Sample(MyText: String);
467464begin
468465 ShowMessageFmt('Hidden String was changed to "%s"',[MyText]);
469466end;
470467
471468procedure TForm1.Button1Click(Sender: TObject);
472469begin
473470 MyVisualComponent1.HiddenString:='Hello World';
474471end;
475472}
476473end.
477474```
478475
479476### Download
480477
481478You can download this code as a package ready to install and play with [here](http://www.charcodelvalle.com/myvisualcomponent/myvisualcomponent_code.zip)
482479
483480### Author
484481
485482minesadorada@charcodelvalle.com
486483
487484### License
488485
489486All code is released under the LGPL license
490487
491488## See also
492489
493490- [[Components and Code examples|Components and code examples]]
494491- [[How To Write Lazarus Component|How to write a Lazarus component]]
495492- [[Object Oriented Programming with FreePascal and Lazarus|Object-oriented programming with FreePascal and Lazarus]]
496493
497494---
498495*From the Free Pascal wiki, page "Lazarus Nongraphical Visual Component Example Code", as it stood on 2020-02-18 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
499496Category: Code Snippets
500497Category: Component Creation
501498Category: Object-oriented development
502499Category: Tutorials

Back to history