HeavyUser's code used getter and setter, that was needed to create a property (see his code
line #13), and he made the property public. Mine cannot be accessed from outside because the code of MyButtonClick (my version) was provided by the class itself.
About object ownership. If an object has an owner, the owner is responsible for freeing the object. That's why they said we don't need to free it manually. It is my habit, to free all the memory and objects I requested/created, including the ones have owner. You don't have to, it is up to you.
However, for learning purposes and better understanding... is it possible to replace TPanel with TImage?
Yes, you can. It depend on what you want to achieve, each component has its own advantages/disadvantages. For example: TPanel supports tabstop but no picture. TImage supports picture but no tabstop. Although TPanel does not support picture by default, sure you can write code for making it to show picture.
There are many different ways to write a component. You can combine some components to became a new one, you can inherit from a component and add some new features, and you can write it from scratch.
I wrote a demo showing how to write a simple component by inherited from TCustomControl. I prefer
TCustomControl because it is lightweight and it already provides many useful features like caption, border, setbound, onclick, etc.
unit MyComponent;
{$mode ObjFPC}{$H+}
interface
uses
Classes, SysUtils, Controls, ExtCtrls, DateUtils;
type
{ TMyComponent }
TMyComponent = class(TCustomControl)
strict private
FTimer: TTimer;
procedure OnTimer(Sender: TObject);
public
procedure Paint; override;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure ShowTime;
end;
implementation
{ TMyComponent }
procedure TMyComponent.OnTimer(Sender: TObject);
var
Current: TDateTime;
begin
Current := Time;
Caption := HourOf(Current).ToString + ':' + MinuteOf(Current).ToString + ':' +
SecondOf(Current).ToString;
Invalidate;
end;
procedure TMyComponent.Paint;
var
PosX: Integer;
PosY: Integer;
begin
inherited;
PosX := (Width - Canvas.GetTextWidth(Caption)) div 2; // Calculate center x
PosY := (Height - Canvas.GetTextHeight(Caption)) div 2; // Calculate center y
Canvas.Brush.Color := Color;
Canvas.TextOut(PosX, PosY, Caption);
end;
constructor TMyComponent.Create(AOwner: TComponent);
begin
inherited;
// Set default values
FTimer := nil;
Caption := 'New';
Width := 80;
Height := 30;
BorderStyle := bsSingle;
if AOwner is TWinControl then
Parent := (AOwner as TWinControl);
end;
destructor TMyComponent.Destroy;
begin
if Assigned(FTimer) then
FTimer.Free;
end;
procedure TMyComponent.ShowTime;
begin
FTimer := TTimer.Create(nil);
FTimer.Interval := 1000;
FTimer.OnTimer := @OnTimer;
end;
end.
Line #16Programmers usually add an F at the beginning of the name of a class's field.
Line #15..#17FTimer and OnTimer should be in private section. I personally prefer
strict private.
Line #19..#22Each of them needs an
override modifier. Check the documentation if you want to learn more.
Line #29OnTimer will be called by FTimer every second.
Line #36Call invalidate to let the system know that the data has been changed and need to redraw its appearance.
Line #39Calculate and show the caption at the center of the component.
Line #61Only TWinControl (and its inheritance) can be a parent.
Line #69If it is created, it should be freed. (but you don't have to free it manually if it has an owner)
Line #72Create a timer for showing time.