At times the standard components provided with Delphi do not give you all the functionality that you require, the aim of this tutorial is to show you how to add and implement extra properties to the TButton control. I did this in Delphi 3 so I will also briefly go into how to create a new Package and add the New component to it. The component is totally compatible with Delphi 2 but not Delphi 1 as it uses some Windows 95 specific messages.
As we wish to keep all the existing functionality of TButton and do not wish to rewrite it ourselves, we will make our component a descendant of the TButton Class hence keeping all of its functionality but still enabling us to add the additional functionality that we need.
The properties that we are going to add are :
|MultiLine||Boolean||By setting this to true the caption on the button will be displayed on more than one line (with word wrap) if the button is not wide enough to hold it.|
|HorizAlignment||THorizAlign||This property can be set to one of three values (which we will define as a new type) its purpose will be to allow the caption to be aligned on the horizontal axis (left, centre or right).|
|VerticalAlignment||TVerticalAlign||This property is very similar to HorizAlignment but its used with regards to the vertical axis (top, centre or bottom).|
We will now have to define 2 new types to be used with setting the different alignment properties, we could of done this by having just an integer value so that different values would mean a different alignment, but by defining a new type it makes your code a lot easier to read and maintain. The 2 new types are defined as follows:
type THorizAlign = (halLeft,halRight,halCentre); TVerticalAlign = (valTop,valBottom,valCentre);
TMultiLineBtn = class(TButton) private fMultiLine: Boolean; fHorizAlign : THorizAlign; fVerticalAlign :TVerticalAlign; procedure SetMultiLine(Value: Boolean); procedure SetHorizAlign(Value: THorizAlign); procedure SetVerticalAlign(Value: TVerticalAlign); protected procedure CreateParams(var Params: TCreateParams); override; public constructor Create(AOwner: TComponent); override; published property HorizAlign: THorizAlign read fHorizAlign write setHorizAlign default halCentre; property VerticalAlign :TVerticalAlign read fVerticalAlign write setVerticalAlign default valCentre; property MultiLine: Boolean read fMultiLine write SetMultiLine default True; end;
The private section contains the new properties and procedures needed for implementing the new component. The protected section overrides the createParams procedure which is used to actually implement the additional functionality, this will be explained later. The create procedure is overriden in the public section, this allows us to set our new properties to their Default values when an instance of our component is created. The properties in the published section will actually appear in the Object Inspector and as we have defined a new type for the alignment properties they will appear as combo boxes allowing easy changing of their value. The way our alignment properties work is, if they are read the value of its equivalent in the private section is returned whilst if the use wishes to change a value the set procedure is called, passing the new value as a parameter, this allows us to update the control visually whenever the value is changed.
constructor TMultiLineBtn.Create(AOwner: TComponent); begin inherited Create(AOwner); fMultiLine :=True; fHorizAlign := halCentre; fVerticalAlign := valCentre; end;
The constructor inherits the create method for a normal TButton, and sets all the default values.
procedure TMultiLineBtn.SetVerticalAlign(Value: TVerticalAlign); begin if fVerticalAlign<>Value then begin fVerticalAlign:=Value; RecreateWnd; end; end;
This procedure is called whenever the VerticalAlign property is changed (both at design and run time). It only does anything if the new value is different to the old one. If the value is different then it sets the Private VerticalAlign (fVerticalAlign) property to the new value and recreates the control with these new settings. (This is explained further in the CreateParams procedure).
procedure TMultiLineBtn.SetHorizAlign(Value: THorizAlign); begin if fHorizAlign<>Value then begin fHorizAlign:=Value; RecreateWnd; end; end;
procedure TMultiLineBtn.SetMultiLine(Value: Boolean); begin if fMultiLine<>Value then begin fMultiLine:=Value; RecreateWnd; end; end;
These two procedures work in the same way as the SetVerticalAlign but changing there respective values.
procedure TMultiLineBtn.CreateParams(var Params: TCreateParams); begin inherited CreateParams(Params); case VerticalAlign of valTop : Params.Style:=Params.Style or BS_TOP; valBottom : Params.Style:=Params.Style or BS_BOTTOM; valCentre : Params.Style:=Params.Style or BS_VCENTER; end; case HorizAlign of halLeft : Params.Style:=Params.Style or BS_LEFT; halRight : Params.Style:=Params.Style or BS_RIGHT; halCentre : Params.Style:=Params.Style or BS_CENTER; end; if MultiLine then Params.Style:=Params.Style or BS_MULTILINE else Params.Style:=Params.Style and not BS_MULTILINE; end;
This is the crunch procedure of our new component. It gets called whenever a button is created and also when one of our new properties is changed (by means of the RecreateWnd call). By checking in the win32 help file I noticed that windows button control had the required functionality which has not been encapsulated in TButton, so all that is needed is to set these values whenever the new button is created or recreated. To start with we inherit CreateParams from TButton thus letting it do any initialisation that it needs to, next depending on what are values for the alignments and multiline are, we OR them with Style property of the Params. Note the BS_ constants are all defined in the windows.pas file. They are ORed because they do not take up all the bits of the style property so by using OR with the constants only the relevant bits are changed. For further information regarding the different Styles supported in the windows API have a look at the Button Styles section of win32.hlp.
As Delphi 3 now supports packages (a different way of storing components which I will go into more detail on in a later article) I thought it would be a good idea to create a new package and add the new button control to it. One of the ways of doing this is to create a new package by selecting New... off the File menu, add from the dialog box select Package. After you enter a name and description for the new package (use your imagination here) it brings up the Package Editor, which shows you what the package contains and allows you to Add and Remove Components, Compile the Package and Install it. The first thing we need to do is Add our new component, so click on Add and Browse to your newly created pas file, click on OK and you will see your unit has been add in the Contains tab, all you need to do now is Compile it, and then Install it, by clicking the relevant buttons. You will find that you now have a new tab on the component palette called Tutorials, with your newly created button control on it, which you can now use as you can all the other components.
From the component menu select Install, this brings up the Install Components Dialog Button box, which shows you what is presently installed and allows you to add new components or remove them. We want to add a new component so click on Add. It now asks you for the module name, this is just the pas file that we have just created, so browse to the correct file. Click OK and you'll notice that the path to the new component has been added to the search path and that its name is in the Installed Units list box. Now click on OK to rebuild the library. You will now have a new tab on the component palette called Tutorials, with your newly created button control on it, which you can now use as you can all the other components.