Difference between revisions of "How To Write Lazarus Component"

From Lazarus wiki
Jump to navigationJump to search
m (Using custom paint procedure)
m (Updated first part of the tutorial with Lazarus 0.9.30 pics.)
Line 5: Line 5:
 
==Step 1: Create The Package==
 
==Step 1: Create The Package==
  
* On the Lazarus IDE menu, click Package > New Package to open the Package Manager. The following image shows the Package Manager
+
* On the Lazarus IDE menu, click '''Package > New Package''' to run the Package Manager.  
  
[[Image:How_to_write_lazarus_component_package_maker.png|Package Maker]]
+
[[Image:package_menu.png]]
  
* Use the Save button at top left.
+
* A '''Save dialog''' file will apear. Chose a folder and a filename and press save. If the IDE promt for using lowercase filenames press 'yes'.
* Depending on your 'naming' setting in the 'environment options', the IDE will ask you to save the file lowercase. Say yes.  
 
  
Congratulations: You have just created your first package!  
+
* Congratulations: You have just created your first package!
 +
 
 +
[[Image:How_to_write_lazarus_component_package_maker.png|Package Maker]]
  
 
==Step 2: Create The Unit==
 
==Step 2: Create The Unit==
  
* Use the Add button -> New component
+
* Use the '''Add button > New component'''.
* Choose a component in the ancestor type combobox. For instance: TCustomComboBox.
+
 
* Choose ''mycom.pas'' as filename
+
[[Image:package_new_component.png]]
* Click Ok
+
 
* The file will be added to the package and opened in the editor. Use something like the following code (The 'Sample' parameter in the RegisterComponents tells the IDE to put the component in a tab called Sample in the components tab)
+
* Choose a component like TComboBox.
 +
* Choose ''customcontrol1.pas'' as filename.
 +
* Click OK.
  
<delphi>
+
<delphi>unit CustomControl1;
unit mycom;
 
  
 
{$mode objfpc}{$H+}
 
{$mode objfpc}{$H+}
Line 30: Line 32:
  
 
uses
 
uses
   Classes, SysUtils, StdCtrls, Forms, lresources;
+
   Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls;
  
 
type
 
type
   TMyCom = class (TCustomComboBox)
+
   TCustomControl1 = class(TComboBox)
 
   private
 
   private
 +
    { Private declarations }
 +
  protected
 +
    { Protected declarations }
 
   public
 
   public
 +
    { Public declarations }
 +
  published
 +
    { Published declarations }
 
   end;
 
   end;
  
Line 44: Line 52:
 
procedure Register;
 
procedure Register;
 
begin
 
begin
   RegisterComponents('Sample',[TMyCom]);
+
   RegisterComponents('Standard',[TCustomControl1]);
 
end;
 
end;
  
initialization
+
end.</delphi>
  {$I samplepackage.lrs}
 
  
end.
+
* Install the package by clicking the 'install' button in the top of the package editor.
</delphi>
 
  
 +
[[Image:package_install.png]]
  
* Install the package by clicking the 'install' button in the top of the package editor.
+
* Then the IDE will ask you, if the IDE should be rebuilt. Say yes.
* Lazarus will ask to save the package, save the package as ''samplepackage.lpk''. Then the IDE will ask you, if the IDE should be rebuilt. Say yes.
+
 
* The packages are statically linked, so a restart of the IDE is needed.
+
[[Image:package_rebuild.png]]
* Restart Lazarus and see your new component in the component palette (For example: A TBevel1 will be on the 'Additional' page).
+
 
* 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 in: Environment -> Environment options -> Files -> Lazarus directory. 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.  
+
* Restart Lazarus and see your new component in the component palette. Congratulations: You have just installed your first package with your first package component.  
  
Congratulations: You have just installed your first package with your first package component.  
+
[[Image:package_installed.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 in: Environment -> Environment options -> Files -> Lazarus directory. 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.
  
 
==Step 2.2: Add an existing Unit==
 
==Step 2.2: Add an existing Unit==

Revision as of 01:06, 28 June 2011

Introduction

This is a basic guide on how to build components. It was tested on Windows XP running Lazarus 0.9.25 beta.

Step 1: Create The Package

  • On the Lazarus IDE menu, click Package > New Package to run the Package Manager.

package menu.png

  • A Save dialog file will apear. Chose a folder and a filename and press save. If the IDE promt for using lowercase filenames press 'yes'.
  • Congratulations: You have just created your first package!

Package Maker

Step 2: Create The Unit

  • Use the Add button > New component.

package new component.png

  • Choose a component like TComboBox.
  • Choose customcontrol1.pas as filename.
  • Click OK.

<delphi>unit CustomControl1;

{$mode objfpc}{$H+}

interface

uses

 Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls;

type

 TCustomControl1 = class(TComboBox)
 private
   { Private declarations }
 protected
   { Protected declarations }
 public
   { Public declarations }
 published
   { Published declarations }
 end;

procedure Register;

implementation

procedure Register; begin

 RegisterComponents('Standard',[TCustomControl1]);

end;

end.</delphi>

  • Install the package by clicking the 'install' button in the top of the package editor.

package install.png

  • Then the IDE will ask you, if the IDE should be rebuilt. Say yes.

package rebuild.png

  • Restart Lazarus and see your new component in the component palette. Congratulations: You have just installed your first package with your first package component.

package installed.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 in: Environment -> Environment options -> Files -> Lazarus directory. 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.

Step 2.2: Add an existing Unit

If you already had a unit mycom.pas, you can add it to the package with:

  • Click the Add button, goto the Add Unit tab. At the Unit file name, browse to the mycom.pas file. Click the Add Unit button to confirm. If the packet manager complains that the unit is not in the unitpath, click yes to add the directory to the unit path.
  • Click the Add button again, goto the Add File tab and browse to the samplepackage.lrs file and click Ok.
  • Click the Add button again, goto the New Requirement tab, in the Package name select LCL (as the base class, TCustomComboBox, uses this) and click Ok.

End result should look like this:

Package Maker
  • Click on mycom.pas under the Files tree in the Package Manager. In the File Properties, make sure Register unit is checked.
  • Click the Options button. Select the IDE Integration tab. At the Package Type make sure Designtime and Runtime is selected.
  • Click the Compile button to check to see if the files compile without errors.
  • Click the Install button, Lazarus will rebuild and restart automatically.

The component is created and ready to be used:

Component Created

Step 3: Create Icons For The Package

  • Size of PNG file should be 24x24

To create the lrs file run (where samplepackage is the name of your package and TMyCom is the name of your component):

~/lazarus/tools/lazres samplepackage.lrs TMyCom.png
  • You may need to compile lazres at first use. Simply open the lazres.lpi in the IDE and at the menu click run > build.

You can add more than one image to the lrs file by appending the image filename at the end. Eg. ~/lazarus/tools/lazres samplepackage.lrs TMyCom.png TMyOtherCom.png ...

Following is a sample of the resulting samplepackage.lrs file.

LazarusResources.Add('TMyCom','PNG',[
  #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
  +#0#0#0#4'gAMA'#0#0#177#143#11#252'a'#5#0#0#0'|IDAT8O'#237#212#209#10#192' '#8
  +#5'P'#247#231#251's'#215#138#133#164#166'\'#220#195'`'#209'c'#157'L'#173#131
  +#153#169'd4'#168'dP'#137'r_'#235'5'#136'@Zmk'#16'd9'#144#176#232#164'1'#247
  +'I'#8#160'IL'#206'C'#179#144#12#199#140'.'#134#244#141'~'#168#247#209'S~;'#29
  +'V+'#196#201'^'#10#15#150'?'#255#18#227#206'NZ>42'#181#159#226#144#15'@'#201
  +#148#168'e'#224'7f<@4'#130'u_YD'#23#213#131#134'Q]'#158#188#135#0#0#0#0'IEND'
  +#174'B`'#130
]);

  • Click the Add button again, goto the Add File tab and browse to the samplepackage.lrs file and click Ok.

Recompiling Packages

You need to rebuild the package everytime 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.

Removing Packages

  • To remove installed components, on the IDE menu, click Package > Configure installed packages. The following image shows the Installed Packages tool.
Installed Components
  • Select the package you want to uninstall and click Uninstall selection.

If something goes wrong with a package (eg 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.

Enhancing mycom.pas

  • The codes 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.
  • The OnChange2 shows how to create events
  • The OnSample shows how to create custom events
  • MyText and MyText2 shows different ways to write properties.
  • You can use TComboBox instead of TCustomComboBox as the base class, which publishes all properties as TComboBox.
  • If TCustomComboBox is used as the base class, you'll notice a lot of properties and events will be missing at the Object Inspector in the IDE. 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.

<delphi> unit mycom;

{$mode objfpc}{$H+}

interface

uses

 Classes, SysUtils, StdCtrls, Forms, Dialogs,
 LCLType,LCLIntf,lresources,LCLProc;

type

 TSampleEvent = procedure(MyText: String) of Object;
 TMyCom = class (TCustomComboBox)
 private
   FMyText: String;
   FOnChange2: TNotifyEvent;
   FOnSample: TSampleEvent;
 public
   constructor Create(TheOwner: TComponent); override;
   procedure CreateWnd; override;
   procedure Change; override;
 protected
   function GetMyText2: String;
   procedure SetMyText2(MyText: String);
 published
   property MyText: String read FMyText write FMyText;
   property MyText2: String read GetMyText2 write SetMyText2;
   property OnChange2: TNotifyEvent read FOnChange2 write FOnChange2;
   property OnSample: TSampleEvent read FOnSample write FOnSample;
   
   // properties from TComboBox
   property Align;
   property Anchors;
   property ArrowKeysTraverseList;
   property AutoComplete;
   property AutoCompleteText;
   property AutoDropDown;
   property AutoSelect;
   property AutoSize;
   property BidiMode;
   property BorderSpacing;
   property CharCase;
   property Color;
   property Ctl3D;
   property Constraints;
   property DragCursor;
   property DragMode;
   property DropDownCount;
   property Enabled;
   property Font;
   property ItemHeight;
   property ItemIndex;
   property Items;
   property ItemWidth;
   property MaxLength;
   property OnChange;
   property OnChangeBounds;
   property OnClick;
   property OnCloseUp;
   property OnContextPopup;
   property OnDblClick;
   property OnDragDrop;
   property OnDragOver;
   property OnDrawItem;
   property OnEndDrag;
   property OnDropDown;
   property OnEditingDone;
   property OnEnter;
   property OnExit;
   property OnGetItems;
   property OnKeyDown;
   property OnKeyPress;
   property OnKeyUp;
   property OnMeasureItem;
   property OnMouseDown;
   property OnMouseMove;
   property OnMouseUp;
   property OnStartDrag;
   property OnSelect;
   property OnUTF8KeyPress;
   property ParentBidiMode;
   property ParentColor;
   property ParentCtl3D;
   property ParentFont;
   property ParentShowHint;
   property PopupMenu;
   property ReadOnly;
   property ShowHint;
   property Sorted;
   property Style;
   property TabOrder;
   property TabStop;
   property Text;
   property Visible;    
 end;

procedure Register;

implementation

procedure Register; begin

 RegisterComponents('Sample',[TMyCom]);

end;

constructor TMyCom.Create(TheOwner: TComponent); begin

 inherited Create(TheOwner);
 Self.Style := csDropDownList;

end;

procedure TMyCom.CreateWnd; begin

 inherited CreateWnd;
 Items.Assign(Screen.Fonts);

end;

procedure TMyCom.Change; begin

 inherited;
 if Assigned(FOnChange2) then FOnChange2(Self);
 if Assigned(FOnSample) then FOnSample(FMyText);

end;

function TMyCom.GetMyText2: String; begin

 Result:=FMyText;

end;

procedure TMyCom.SetMyText2(MyText: String); begin

 FMyText:=MyText;

end;

initialization

 {$I samplepackage.lrs}

end. </delphi>

Using embedded (visual) components

It's possible to use standard components embedded in your own components (look for example at TLabeledEdit or TButtonPanel). 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 to add a TLabel to the component, do the following:

  • Add a private attribute for the label component (FEmbeddedLabel: TLabel;).
  • Add a published read-only property for the label component (property EmbeddedLabel: TLabel read FEmbeddedLabel;)
  • Create the label in the component's (overridden) constructor (FEmbeddedLabel := TLabel.Create(self); )
  • Set the parent of the component (FEmbeddedLabel.Parent := self;)
  • 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);).

To sum it up you would get something like this (only the essential parts are shown):

<delphi> TEnhancedPanel = class(TCustomControl) private

 { The new attribute for the embedded label }
 FEmbeddedLabel: TLabel;
 

public

 { The constructor must be overriden so the label can be created }
 constructor Create(AOwner: TComponent); override;
 

pubished

 { Make the label visible in the IDE }
 property EmbeddedLabel: TLabel read FEmbeddedLabel;

end;

implementation

constructor TEnhancedPanel.Create(AOwner: TComponent); begin

 inherited Create(AOwner);
 // Set default width and height
 with GetControlClassDefaultSize do
   SetInitialBounds(0, 0, CX, CY);
 // Add the embedded label
 FEmbeddedLabel := TLabel.Create(Self); // Add the embedded label
 FEmbeddedLabel.Parent := self;         // Show the label in the panel
 FEmbeddedLabel.SetSubComponent(true);  // Tell the IDE to store the modified properties
 FLabel.Name := 'EmbeddedLabel';        
 FLabel.Caption := 'Howdy World!';
 // Make sure the embedded label can not be selected/deleted within the IDE
 FLabel.ControlStyle := FLabel.ControlStyle - [csNoDesignSelectable];
 
 // Set other properties if necessary
 //...
 

end; </delphi>

Using custom paint procedure

Yu can always subclass a component inside your program. For example, this implements a custom Paint procedure to a TLabel:

<delphi> type

 TMyLabel = class(TLabel)
   public
     procedure Paint; override;
 end;

{...} implementation {...} procedure TMyLabel.Paint; begin

 // your code to implement Paint, for example
 Canvas.TextOut(0,0,Caption);

end; </delphi>

Now you can create a MyLabel inside your program, at run time, with that overridden Paint procedure instead of the standard one.

Reference

You can post question regarding this page here

Author

 Original Author