Delphi - TFrame Components
Creating components from scratch is covered
on other pages.
However, sometimes it is useful to
reuse a collection of existing components and related code.
There are several ways to do this, each with its own pros and cons.
This page discusses of the TFrame method.
Note: |
| There is something weird about the technique described on this page.
When I wrote it, this was correct - however, now (07-2014) it is not.
I am trying to figure out what happened .. and why.
|
Basics
| Technique
| Naming Issue
| Inheritance
| Initialization
| Multiselect Handles
Basics
A TFrame is designed just like a form
- You can place any component from the toolbar in the frame
- You can modify the size, position, and properties in the design window
Advantages (with respect to a custom component)
- When the frame is added to a form, the developer has complete access to
the published properties and events of each sub-component.
- The properties of the sub-components are stored in the associated *.dfm file
Disadvantages (with respect to a custom component)
- There is no Register procedure.
Not a big problem, just add one.
- Users can delete existing components from the frame.
- If you aren't very careful, when using a mouse, you will end up moving
something inside the frame instead of the frame itself.
(Very frustrating.)
TFrame and TForm are similar in many ways.
They are both descendents of TScrollingWinControl and
both are associated with dfm files - text files that define the properties
associated with buttons and other controls.
The TFrame does not have a border (BorderStyle) or the OnCreate event.
Otherwise, they are similar.
Technique
- From the menu, select File / New Frame
- Add components and code just like you were designing a form
- Right click the form (frame) and select Add to Palette
- You will be required to Save it first
- You are prompted for a Component name, Palette page, and Palette icon
Palette page is the toolbar tab it will be placed on ..
the default is Templates.
The icon can be changed to any 24x24 bitmap (*.bmp file).
The original class name was TFrame2 .. this prompt changes it to what you enter ..
sort of.
Apparently, the word Template will also be associated with it.
I am not sure what function (if any) this performs.
(The number in the default name depends on the number of unsaved units that are open.
The default form is '1', the frame is '2'.)
Naming Issue
When a TFrame is placed on a form, the default name for the
first occurrence starts with a "T", the later occurrences do not.
type
TForm1 = class(TForm)
Chart1: TChart;
mcINI_File1: TmcINI_File;
Panel1: TPanel;
TmcLegendElement1: TmcLegendElement; // <- this is wrong
mcLegendElement2: TmcLegendElement; // <- this is correct
|
Though it doesn't really hurt anything .. this is a Delphi design problem.
Inheritance
Frames are special types of objects.
At design time, the user is able to select individual components
and set their properties .. including the events.
The main reason I use frames is so that some fairly complex code can
be reused without having to test it in every application.
With regular objects,
when a method is overridden,
the underlying code is executed using inherited.
However, TFrames work a bit differently
because the event code is owned by the frame - not the object.
The following code (automatically created when double clicking a component on the frame)
shows how the inherited code is called.
procedure TForm1.TmcTemperature_UIFrame1Temp_K_UImcSpinEditChange(
Sender: TObject);
begin
TmcTemperature_UIFrame1.Temp_K_UImcSpinEditChange(Sender); // automatically added
// my code goes here
end;
|
Initialization
One of the important differences between TFrame and TForm
is the OnCreate event - TForm has it, TFrame doesn't.
One solution is to override the frame's Loaded method.
(TComponent.Loaded should be left protected.)
TmcTemperature_UIFrame = class(TFrame)
Temp_C_UImcSpinEdit: TmcSpinEdit;
procedure Temp_C_UImcSpinEditChange(Sender: TObject);
protected
procedure Loaded; override;
end;
procedure TmcTemperature_UIFrame.Loaded;
begin
inherited;
Temp_C_UImcSpinEditChange(nil);
end;
|
This is better than having to remember to place something in the form's
OnCreate event.
However, there are 2 items to be aware of
- While this code will run after all the frame related properties are set,
it runs before some of the other form components are loaded.
- This routine might be called more than once.
Multiselect Handles
At design time, when a control is selected,
8 black sizing handles (squares) are displayed.
When several controls are selected, 4 grey handles are displayed.
For a number of containers that accept components (frames, forms, panels, and the like),
the 8 black handles are rendered on top of the sub-components ..
but the 4 grey handles are displayed behind them.
As a result, if a frame (or similar container) has components in the corners,
the grey handles are very difficult to see.
Therefore,
it is important that the frame be a bit larger than the components in it.
In particular, none of the components should touch the corners of the frame.
A border of 2 or 3 pixels makes a big difference.
Author: Robert Clemenzi -
clemenzi@cpcug.org