Tutorial - TActBTn (visual)
Writing your first component (part 3)
WRITING PROGRAM VISUAL STYLE
1 * Purpose of having button type ActBtn(4/78).
2 * ControlStyle and indication Component behaviour(4/79).
3 * Adding shadow to your component button with TShape(4/80).
4 * Creating 2 Instance to save TBitmap.(4/82)
5 * Constructor and Destructor Method(4/82)
6 * SetActivePic and SetInActivePic(4/83).
7 * WM_XXXX message (intercept window message)(4/84)
8 * Resize your component TActiveBtn(4/85)
9 * Method of ChangePlace (In ComponentState) Move
(4/86)
10 * CM_XXXX message which effected our component
directly (4/86-87)
11 * Adding Event (4/87)
12 * Important method for TActiveBtn(4/89)
13 * Parent Control and SetParent(4/89-90)
14 * Loaded Method and ReDeclare(4/91)
1 * Purpose of having button type ActBtn(4/78)
TActBtn can be managing by using Component Wizard to do the similar
thing as we did for NewComp. We need original control or control
which different from other people, so we create component from TCustomControl
and name it "Active" because it's similar to Active-X
The good side of TCustomControl is that we could have Canvas Property.
TActBtn is a descendant of TCustomControl.
|
Type
.......TActiveBtn = class(TCustomControl)
End;
|
|
Contstructor TActiveBtn.Create(AOwner:TComponent);
....Begin
.......inherited Create(AOwner);
.......ControlStyle :=ControlStyle + [CsOpaque];
.......Width :=50;
.......Height :=50;
End;
|
Back to
TOP
2 * ControlStyle and indication of Component
behaviour(4/79)
ControlStyle is an Enumerate Type and this ControlStyle is a SET OF ENUMERATE.
Sample of Enumerate set of Virus
|
TVirusName = (Azusa, DieHard, Israel, Mahock, Stelth, ADCD);
TVirus = Set of TVirusName;
Var Virus:TVirus;
Begin
.......Virus := Virus + [Asuza, Diehard, ACDC];
End;
|
Back to our ControlStyle again (off from virus sample enumerate)
|
ControlStyle := ControlStyle + [CsAcceptsControls];
|
Above code enable other Controls to cling to control that we had
just created. (except that our Control is a descendent from control
which already enable this feature).
See below CODE and below ControlStyle behavior
|
ControlStyle := ControlStyle + [CsAcceptsControls , CsOpaque];
|
|
ControlStyle
|
Component behavior which effect from it's value
|
|
csAcceptsControls
|
Enable control to be cling to (to be parent)
|
|
csCaptureMouse
|
Follow any mouse move action caused by users
|
|
csDesignInteractive
|
Enable mouse RIGHT CLICK when designing
|
|
csClickEvents
|
Take care of Control Mouse click event
|
|
csFramed
|
Control which has border 3D
|
|
csSetCaption
|
Translate or interpret caption to match (when put on form)
|
|
csOpaque
|
Fast draw becuase you don't have to delete background
|
|
csDoubleClicks
|
Control handle mouse DOUBLE CLICK
|
|
csFixedWidth
|
Control with fixed or certain width which can't change
|
|
csFixedHeight
|
Cotrol with fixed or ceratin height which can't change
|
|
csNoDesignVisible
|
Drop on form and Invisible (why do we need this, don't know?)
|
|
csReplicatable
|
Repeat draw itself on other control (which allow draw)
|
|
csDisplayDragImage
|
When control moves or drag, change Mouse Cursor and send
signal to user automatically with WM_MOVE
|
|
csReflector
|
Enable this control to be use with Active-X (case Active-X)
|
|
csNoStdEvetnts
|
Let control ignore all Mouse events to speed up program cycle
|
Back
to TOP
3 * Adding shadow to your component button with
TShape(4/80).
You can create Component bitmap with shadow by using TShape. TShape
is a TGraphicControl. The best way to do this is when you are doing
Create Constructor.
Sample
|
Constructor TActiveBtn.Create(AOwner:TComponent);
....Begin
......inherited Create(Aowner);
......ControlStyle :=ControlStyle + [CsOpaque];
......FShape
:= TShape.Create(Self);
......FShape.Brush.Color
:= ClBlack;
......FShape.Visible
:= False;
......Width := 50;
......Height :=50;
......FShape.Width
:= 50;
......FShape.Height
:= 50;
End;
|
FShape is a Component not Class, so we need to indicate Owner and
in this case is "self". You can create by simply
using .Create
You need to also create Instance name FShape
(Class descendant from TShape Component)
|
Type
.......TActiveBtn = class(TCustomControl)
.......private
..........FShape
: TShape;
|
Since TShape is declared in UNIT name ExtCtrls.pas
so we need to refer ExtCtrls under Uses.
|
unit ActBtn ;
interface
.......use
..........Windows, Messages..........
|
Back
to TOP
4 * Creating 2 Instance to save TBitmap.(4/82)
Your Component Now have 2 bitmap (1 normal and 1 with shadow). In
order to save this 2 picture, you need to create 2 Instance at Class
TActBtn.
In Private Section adding this code.
|
Type
.......TActiveBtn = class(TCustomControl)
.......private
.........FShape : TShape;
.........FActivePic
: TBitMap;
.........FInActivePic
: TBitMap;
End;
|
Back
to TOP
5 * Constructor and Destructor Method(4/82)
We mentioned this many times that Instance before calling for use
we need to manage Constructor method. The Best way to do this is
when you are creating your Component.
|
Constructor TActiveBtn.Create(AOwner:TComponent);
....Begin
......inherited Create(Aowner);
......FActivePic
:= TBitmap.Create;
......FInActivePic
:= TBitmap.Create;
......ControlStyle :=ControlStyle + [CsOpaque];
......FShape := TShape.Create(Self);
......FShape.Brush.Color := ClBlack;
......FShape.Visible := Flase;
......Width := 50;
......Height :=50;
......FSahpe.Width := 50;
......FShape.Height := 50;
End;
|
Create Constructor follow by Destructor Method. Notice that we destroy Object before we
destroy our Component always, or we destroy every thing before Inherited Destroy. Or else you could
face Error GFP (general fault protection).
|
Destructor TActiveBtn.Destroy;
....Begin
.......FActivePic.Free;
.......FInActivePic.Free;
.......inherited Destroy;
End;
|
Back
to TOP
6 * SetActivePic and SetInActivePic(4/83).
We need to handle FActivePic and FInactivePic with 2 methods which
are "SetActivePic" and "SetInActivePic". (normally
we put in private section).
SetActivePic indicates Bitmap when MouseMoveIn, and SetInActivePic
indicates Bitmap when MouseMoveOut.
|
Type
...TActiveBtn = class(TCustomControl)
...Private
...................
...................
........FActivePic : TBitMap;
........FInActivePic : TBitmap;
........Procedure SetInActivePic
(Value:TBitmap);
........Procedure SetActivePic
(Value:TBitmap);
....Published
........Property PictureWhenActive:TBitmap Read FActivePic Write
SetActivePic;
........Property PictureWhenInActive:TBitmap Read FInActivePic Write
SetInActivePic;
End;
|
then follow by below code.
|
Procedure TActiveBtn.SetActivePic (Value:TBitmap);
...Begin
........FActivePic.Assign(Value);
<assign can't use equal sign = becuase
= use for instance only>
........Width
:= FActivePic.Width;
........Height
:= FActivePic.Height;
........Invalidate;
<Draw itself and act on screen when
design immediately>
End;
Procedure TActiveBtn.SetInActivePic (Value:TBitmap);
...Begin
........FInActivePic.Assign(Value);
........Width := FInActivePic.Width;
........Height := FInActivePic.Height;
........Invalidate;
End;
|
* You can add more effects as above code such as FShadowColor,
FShadowRight, FState and so on.
Back
to TOP
7 * WM_XXXX message (intercept window message)(4/84)
Delphi has Encapsulated Windows API intercept window message. These value is an Constant starting with WM_XXXXX.
All these message value in HEXA. These message is use with only windows control (TWinControl and TCustomControl).
It can be use partially if it is TGraphicControl.
Example if you move your component button TActBtn, it will intercept
message WM_MOVE but if it is being enlarge or reduce in size , it's
intercept message WM_RESIZE. Delphi make this
easy by allow adding in Method anywhere in Class (Recommended put
this in Protected Section for future call for use).
|
Procedure WMResize(Var Msg:TMessage); Message WM_size;
|
Back
to TOP
8 * Resize your component TActiveBtn(4/85)
Plan ahead what happen if TActBtn or your Component is stretch or
resize. Base on our sample of 2 bitmap 1 normal and 1 with shadow,
then we have to consider when it's stretch or resize, what will
happen to Bitmap shadow?
|
Procedure TActiveBtn.WMResize (Var Msg:TMessage);
....Begin
.......inherited;
.......FShape.Width := Width;
.......FShape.Height := Height;
.......FShape.Top := Top + FShadowBottom;
.......FShape.Left := Left + FShadowRight;
End;
|
Above code should do the job handling shadow when it's stretch
or resize.
Back
to TOP
9 * Method of ChangePlace (In ComponentState)
Move (4/86)
You should also intercept window message of ChangePlace or Move
WM_MOVE as well. Base on our sample shadow should move as well.
In Method of ChangePlace we intercept while designing (CsDesigning)
which is in ComponentState.
|
Procedure TActiveBtn.ChangePlace (Var Msg:TWMMove);
....Begin
....if CsDesigning in ComponentState then
....Begin
.......inherited
.......{
.......Left := Msg.Pos.x;
.......Top:= Msg.Pos.y;
........}
.......FShape.Top := (Msg.Pos.Y-1) + FShadowBottom;
.......FShape.Left := (Msg.Pos.X-1) + FShadowRight;
.......End;
End;
|
Back
to TOP
10 * CM_XXXX message which effected our component
directly (4/86-87)
CM_XXXX is message direct to your Component such as CMMouseEnter, CMMouseLeave, WMDoubleClick, WMButtonDown, WWButtonUp.
|
Procedure CMMouseEnter (Var Msg:TMessage); Message CM_MouseEnter;
|
If mouse move to control, then there is a CM_MouseEnter then check if it's
was assigned. If assigned then draw using command Canvas.Draw
Remember TCustomConrol have "Canvas" so we can draw using
Method to draw. Draw indicates where you want to draw starting at
0, 0 (top left of our component).
It would be best to have both CMMouseEnter and CMMOuseLeave.
|
Procedure TActiveBtn.CMMouseEnter (Var Msg:TMessage);
....Begin
....if Assigned(FActivePic)
then Canvas.Draw (0 ,0 , FActivePic);
....End;
Procedure TActiveBtn.CMMouseLeave (Var Msg:TMessage);
....Begin
....if Assigned(FInActivePic)
then Canvas.Draw (0, 0, FInActivePic);
....End;
|
Back
to TOP
11 * Adding Event (4/87)
Now we are going to adding 2 events. One event for real pressing
on button, and one event to sink the button (illusion) without actually
pressing the button. We are going to call it using METHOD style
"PROCEDURAL POINTER".
Event is property, so writing is the same but in
PUBLISHED section, so that it shows in Object Inspector.
|
Type
.......TState = (ActDown, ActUp);
.......TMouseOrKey = (ByMouse, By Key);
.......TOnDownEvent = Procedure
(Activate : TMouseOrKey) of Object;
.......TOnDrawDownEvent = Procedure
(Sender : TObject) of Object;
|
Event handler here is when a mouse is CLICK on control.
|
Procedure TActiveBtn.WMLButtonDown (Var Msg:TMessage);
....Begin
....inherited;
....If FState <> ActDown then
........Begin
...........OrgTop := Top;
...........OrgLeft := Left;
...........Top := FShape.Top;
...........Left := FShape.Left;
...........If Assigned (FOnDownEvent) then
...............Begin
...................FOnDownEvent (ByMove);
...................End;
...........FState := ActDown;
...........End;
...........if FStayDown = Flase then WMLButtonUp (Msg);
....End;
|
Back
to TOP
12 * Important method for TActiveBtn(4/89)
Everyone likes the sensation (illusion effects) of interactive Button.
Important method that does the DRAW for general control is Method
name PAINT that is always Virtual or Dynamic in descendant.
If you wish to change the look of your component, then you have
to draw by using command draw name TCanvas by using Override Method
name Paint. The easiest way is to draw and save in file Resource.
In this sample, we will see method command Canvas (Important for
our Component).
Procedure Paint ; Override ;
Override need inherited always, remember?,
see below
|
Procedure TActiveBtn.Paint;
....Begin
.......inherited Paint;
.......If Assigned (FInActivePic)
then Canvas.Draw (0, 0, FInactivePic);
End;
|
Back
to TOP
13 * Parent Control and SetParent(4/89-90)
Parent Control - When you drop 1st Control on top of another
Control and unable it to move out from the first control means the
first Control is a Container or Parent Control of other Control.
|
|
Form1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Parent Is Form
|
|
Parent Is Form
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SetParent Control - In Delphi Method Name SetParentControl indicate
that our current Component (Here is TActiveBtn) will cling to which
control (which parent).
Base on our sample Shadow of component will follow parent too (keep
illusion).
|
Procedure TActiveBtn.SetParent (Value:TWinControl);
....Begin
.......If Not (CsDestroying in ComponentState) and (FShape <>
Nil) then
...........Begin
...............FShape.Parent := Value;
...........End;
.......Inherited SetParent (Value);
End;
|
Back
to TOP
14 * Loaded Method and ReDeclare(4/91)
Loaded Method - Before Form can be in use (millisecond before
Form show), it will need to pass through Method name Loaded. This
is the time that Form will load at the same time with our component.
|
Procedure TActiveBtn.Loaded;
....Begin
.......inherited Loaded;
.......If FInActivePic <>
Nil then Canvas.Draw (0, 0, FInActivePic);
.......If FPushShadow then
..........FShape.Visible := True
......else
..........FShape.Visible := False;
......OrgTop := Top;
......OrgLeft := Left;
......FState := ActUp;
End;
|
Redeclare - We could write other property which already
there to be use in Published Section. Properties we are talking
here are such as ShowHint, Left, Top, and etc.
Event such as ONMouseDown, and OnMouseUp are already existed as
well. All these to be REDECLARE , so that they all will appear in
OBJECT INSPECTOR.
If you like to write (create) a component, you might add new method
to your component. Now you know the STEP
of how to Create your Component, but you as a programmer need to
do your own DANCE.
Back
to TOP
|