Design Time Active Components

Theo Hupkens

Most components that are placed on a Form are not active until the program that uses those components runs. With being active I mean producing noise, showing an animation et cetera. It is rather simple to build components that are already active within the IDE, as soon as they are placed onto the form. If you are afraid that active components might take up too much processor capacity, while developing the program you can always use another approach, for instance by adding a menu item to the 'context menu' that activates the component for a short period of time. Later in this paper I shall describe a component that uses this approach.

First I shall describe an illuminated news trailer component (TNewsTrailer). It seems logical to derive this component from TStaticText or TLabel. I prefer the latter, because then also transparent text can be used. The name of a component should always start with an extra character T, so that Delphi can use this name without the extra character T on hints (see illustration).

The news trailer component contains a property TimeStep that controls the velocity and a property Direction that controls whether the text goes from right to left or vice versa. For these properties corresponding variables must be declared in the private section (see listing). We need a timer to control the speed of the news trailer. This timer should be declared in the private section, because we don't want the user to be able to change the settings of the timer directly. TTimer is declared in the unit ExtCtrls, so this unit must be added to the list of units in the uses list. As soon as TimeStep is changed, the timer interval must be changed accordingly. This is done in the procedure SetTimeStep(Value: integer). If a user of a component wants to change any of the values of a property, (s)he cannot do that directly using the private variables. Developers of components, however, should use the private variables (almost) exclusively because otherwise a severe stack overflow error might result. The timer must be created in the constructor of the component and the component must be the owner of the timer. You should NOT use TTimer.Create(AOwner) however, because here AOwner is the owner of the component, not the component itself. You can use TTimer.Create(nil). The Timer must be disabled after its creation. Every property should get its default value in the constructor except for properties that have a default value that corresponds to the binary value zero. I presume that the rest of TTrailer is clear from its listing.

The next component, a waving text, is much more complicated. The component is derived from TGraphicsObject. Again there is a timer. This timer controls how many times the text is written. There is another property that determines how much the text is changed per clock tick. We have a wave length property that sets the number of characters between two nodes of a wave. The property Autosize determines whether the window sizes automatically to the size of the text. If both AutoSize and Transparent are false, the text is written in a colored rectangle. This makes sense only if the text can be aligned within the rectangle. For this purpose a property Alignment is added. Since all characters are written independently, it can happen that some characters (especially if italic fonts are used) overlap a little. For this reason I have made a property that controls the distance between individual letters (measured in pixels). An additional advantage of this property is that moving text can be made more readable by using a somewhat larger distance than usual. Please note that the Windows function GetTextMetrics is used rather than the font property height. The reason for this is that the height property sometimes doesn't give the complete height of the text (it depends on the sign of the size property). Now that every character must be written individually, it is not difficult to change the colors of each character. For this purpose I have included a property Colors, which defines all colors used. There is another property that controls how many colors are actually used. There may be more elegant ways, but this method is simple and will do for most purposes. The maximum number of different colors that can be used can be easily extended if desired. If the number of colors used is zero, then not Color1 is used, but the color of the font, because this is more logical for monochrome text. This all is very beautiful in Delphi: the colors are properties of the class TColor and this class can be used as a property of the class TWavyText. This way we get automatically a pull down menu in the Object Inspector and we get the standard Windows color picker without having to write a single line of code. This component was written as an example of what you can do, you can add many beautiful things to it if desired. For instance the movement can be changed into fancier movements.

If you want these components to live in the design phase already, simply leave on the Timer after creation, but only in the design phase:

If not (csDesigning in ComponentState) then Timer.Enabled := false;

There is a potential risk, however, that something happens before the component completely exists, so it is better to turn the timer on in the Loader procedure (see the Delphi documentation - this subject is out of the scope of this paper).

Now I shall briefly describe two aspects of another animated component: TFilm. First I shall describe how items can be added to the context menu (id est the menu that appears when you right-click on a component that is placed on a form) and second I shall say a few words about an alternative way to change the value of properties.

It is rather simple to add extra menu-items. First you must define a so-called component editor. This component editor should be derived from an already existing component editor (for instance TComponentEditor). Next the functions GetVerbCount, GetVerb and ExecuteVerb must be overridden. GetVerbCount returns the number of extra menu-items, so Result := 4 (if 4 new items are added). The parameter Index of GetVerb and ExecuteVerb correspond to the number of the item starting with the number zero. GetVerb should return the desired caption of the menu-item. In ExecuteVerb you must specify which action must be performed. However, the desired action almost always does something to the component, but it is not known at this time what the name of the component will be. Fortunately we can solve this problem by using the fixed name "Component". Since the class cannot know what the type of this component is, we have to convert it explicitly, in this case to TFilm:

With TFilm(Component) do Begin

// place your code here.

end;

Finally a component editor must be registered: RegisterComponentEditor(TFilm, TFilmEditor).

The way in which properties get their values at design time is determined by a property editor. The property editor must be derived from one of the default editors, for example TSoundProperty = class(TStringProperty).

Two methods must be overridden: GetAttributes and Edit; GetAttributes returns the kind of editor we are going to use. In this case I want to choose a sound file by means of a dialog, so Result := [paDialog]. The procedure Edit does het real job, it creates and opens the dialog, that lets you choose sound files. The result (in this case the name of the file) should be passed through SetValue, but only if the user has clicked the OK-button:

If SoundDialoog.Execute then SetValue(SoundDialoog.FileName);

The class TSoundProperty must be registered. In this case it is used for the property named Sound, so

RegisterPropertyEditor(TypeInfo(AnsiString), TFilm, 'Sound', TSoundProperty);

Since the type of this property is a string, the filename will be automatically stored in the executable. In programs that use TFilm all filenames are saved, but NOT the data in these files. So if such a program is distributed, all data files must be distributed as well and the program must be able to find them. One obvious extension of TFilm would be a mechanism to store the sound and image data together with the program that uses TFilm. There are also some advantages of not storing the data, however (for instance, the programs are small and the data files can be shared by several programs).

Then, finally, a few words about the component TClock.

This component is based on an extended version of TShape. This version makes possible changing both the foreground and background pattern and color and includes a few new shapes. A disadvantage of this method is that sometimes the complete background is repainted while not strictly necessary. This happens at least once per second if show seconds is true.

All components described here can be used and changed freely. However, they have not been tested thoroughly. Under no circumstances shall I be held responsible for any damage or loss of data when using these components.

In order to run the demo it might be necessary to change the directory to the components (use Options/Directories/Conditionals).

I'm sorry, but for the moment only the Dutch versions of the demo and these components are available (because of lack of time I'm afraid).