
XCustomControl class (in XControls.pas unit)
XCustomControl class is derived from XControl and is intended to be
a base class for all self-painting controls, which are not windowed. Such controls are
similar to TGraphicControl in VCL and do not need in window handle, using parent window
(form) for showing. But those all require XCanvas to perform painting and must be painted
itself. Also, its are not using standard Microsoft Foundation Classes to do what its have
to do. So, code of applet, which contains such controls, can be larger, then in case of
using only MFC-based controls. And creating of new self-painted control can be more
complicated task, then creating of mfc-based ones.
Therefore, self-painted controls have some additional advantages.
First, its are created by us and not by Windows creators. So, we can make its more clever,
using more advanced algorithms to perform some works (e.g., handling of huge data arrays).
Or, we can perform painting without flickering, using rotated text (MFC-based controls can
not draw rotated text correctly), custom backgrounds and so on.
It is also possible to combine self-painted and mfc-based controls
together on one form (but this will increase applet size).
XCustomControl = class(XControl);
Properties, inherited from XControl:
ParentForm : XForm;
- returns parent form (i.e. Parent if it is a type of XForm, or ParentForm of
parent control). I.e., parent form is defined as top level parent XClass and to provide
parent form for control, it must be created with parameter, which is not nil, but is XForm
or XControl having ParentForm. Otherwise (when in call of constructor parent is nil)
XCustomControl is created with no parent and can not receive mouse or keyboard events,
because it is not windowed, and such events and focusing can be simulated only using
add-ons XKeyboardEvents and XMouseEvents. |
- OwnerForm : XForm;
- owner form is used for painting only. When XCustomControl is first created, its
OwnerForm gets value from its ParentForm, which can be nil, if its constructor is called
with nil as parameter AParent, or if ParentForm of its parent is nil. It is possible later
to assign value to OwnerForm, and Canvas of OwnerForm is using when XCustomControl is
painting.
To get know, when and how to change and use OwnerForm, look
implementation of some complex controls combined from other ones (for example, XButton,
XGroup, XCheck and so on). |
Showing : Boolean;
- returns True if Visible=True and all parents are visible (including parent
form). |
- Focused : Boolean;
- returns True if the control is 'current' in form. Set it to True to focuse it.
Focused controls receive keyboard input and can change its appearance. Remember, that in
XCL (vs VCL) self-painting non-windowed controls (derived from XCustomControl) also can be
focused (focusing is only emulating by redirecting keyboard input from parent form, which
is actually focused in that case). |
- Align : XAlign;
type XAlign = ( alNone, alLeft,
alTop, alRight, alBottom );
- aligning factor. Takes no desired effect if add-on XAligns.pas is not used in applet (by call
UseAligner). |
TabOrder : Integer;
- creation order of control and it is used also to define tabulating order, in
which controls are enumerated when user tabulate between its using keyboard (if XKeyboardEvens.pas unit is attached).
Take in attention, that in XCL there is no possible to change TabOrder after creating
controls. |
Properties, inherited from XVisual:
- Enabled : Boolean;
- Default is True. Set it to False to disable focusing (and keyboard input) and
all mouse events for this object. |
Visible : Boolean;
- Default is True. Set it to False to void painting of object (form, control) and
taking its rectangle into account while autoplacing of aligned controls. |
BoundsRect : TRect;
- Use it to change position and size of visual object by the single operation to
avoid flicks (instead of consequence assignments of values to follow Left, Top, Width and
Height properties). |
- Left : Integer;
- Top : Integer;
- Width : Integer;
- Height : Integer;
Position : TPoint;
- Use it to change position of visual object with a single operation instead
assigning values to Left and Top properties to reduce resize and realign operations and to
avoid flicks. |
Color : XColor;
- This property is intended here because the most of descenders of XVisual need in
it. But it is made protected to show it in further classes, derived from XVisual. |
Properties inherited from XClass:
Parent : XClass;
ChildCount : Integer;
Children[ Idx : Integer ] : XClass;
XCustomControl methods:
- procedure PaintTo( C : XCanvas; Rect : TRect ); virtual;
- this method is intended to paint self-painted control. Override it to perform its
painting. Look also Tasks for more info. |
Methods, inherited from XControl:
- function ControlAtPos(
X, Y : Integer; IgnoreDisabled : Boolean ) : XControl;
- similar the same function of XForm, it returns
children control, located at the given position (only Visible controls are taking into
consideration, and if IgnoreDisabled is True, only Enabled ones). If two children controls
are overlapped, last created is returned. |
function ClientToScreen( P : TPoint ) : TPoint;
- transforms point coordinates from bounding area of control to screen, taking
into consideration bounds from parent control (so, if Parent is nil, or ParentForm is nil,
this function has no sense). |
function ScreenToClient( P : TPoint ) : TPoint;
- similar function above, converts screen coordinates of given point to bounding
coordinates of control. These both converting functions are usually not used by controls
and if not called in your code, are not inserted into builded applet. |
- function AvailableClientRect
: TRect;
- similar the same function of XForm object, it is intended to return the rest of
client area after applying all left-right and top-bottom alignments if any - to get know
bounds rectangle for control aligning with alClient attribute. In real, this function just
call event On_GetAvailableClientRect of global object GlobalFormsManager if it is
assigned. And this event is assigned only if add-on unit XAligns.pas is used in applet (by callign
UseAligner). |
function CanFocus
: Boolean; virtual; abstract;
- this abstract function must return True or False constantly for given class,
derived from XControl (i.e., if it was already returned True, it is advisable to return
True forever). |
procedure SetOwnerForm(
Value : XForm ); virtual;
- this protected procedure is providing assigning a value to OwnerForm property.
It is better to override it in complex self-painted controls (derived from
XCustomControl), including other controls not as children but as private members, created
without parent. In overriden method call inherited one and extend it by assigning Value to
property OwnerForm of such member controls to get possible to paint those. See example in
XButton (e.g.) implementation. |
|
- Following three procedures are intended to provide controls even non-windowed
with ability to respond on mouse events. Therefore, mouse events are ignoring for XForm if
add-on unit XMouseEvents.pas is not used in your applet (by calling UseStdMouse). If this
last is not used, applet is smaller a little, but self-painting controls can not respond
to mouse events, because three methods below never called. |
procedure DoMouseMove(
Shift : XShiftState; X, Y : Integer ); virtual;
procedure DoMouseDown(
Button : XMouseButton; Shift : XShiftState; X, Y : Integer ); virtual;
procedure DoMouseUp(
Button : XMouseButton; Shift : XShiftState; X, Y : Integer ); virtual;
|
- Like mouse processing procedures above, next three procedures are called in
respond to keyboard events (for currently focused control, if any). But this is occuring
only in case if add-on unit XKeyboardEvents
is used in applet (by calling UseKeyboard). |
procedure DoKeyDown(
Key : Word; Shift : XShiftState ); virtual;
procedure DoKeyUp(
Key : Word; Shift : XShiftState ); virtual;
procedure DoKeyPress(
Key : Char ); virtual;
procedure Place( Control : XControl; NewPos : XPlacePos;
Align : Boolean );
type XPlacePos = ( ppRight,
ppLeft, ppBottom, ppTop );
- this procedure can be called in your applet to simplify allocation of visual
space of form between controls. As it was said earlier, XForm can not read its initial
state from executable. So, it is necessary to create controls manually and set its initial
state, including size and position. This procedure can help to do it, allowing place
controls in desirable visual order instead of changing its BoundsRect property.
Parameter Align if it is True is used to align left or top side of placing
control with correspondent side of base Control, (which side is aligning, depends on
NewPos: for ppRight and ppLeft, top side is aligned, and for ppTop and ppBottom, left side
coordinately).
If You are using add-on XControlAutoPlace,
You may call this method only to start allocating of newly created control from new
vertical column (because the add-on provides placing of new control below of bottom
control). |
Methods, inherited from XVisual:
function CreateWindow
: Boolean; virtual;
- This function do nothing here excluding call of CreateChildWindows and returns
False. When new windowed XVisual descender is designing, it must override this
function to create its window.
Thus XCustomControl is not windowed, You have not to override this
method when deriving new class from it. |
procedure CreateChildWindows;
- calls CreateWindow for all childs of the XVisual object.
Though XCustomControl is not windowed, it therefore can have windowed
children controls. |
function ClientRect
: TRect; virtual; abstract;
- This method is declared as abstract to prevent creating of instances of XVisual
class. Descenders of XVisual (XWindow and XControl)
are implementing this method by the way inherent in each of these two.
ClientRect usually is less or equal to BoundsRect
in size (and for XCustomControl it is originating from topleft corner of the bounding
rectangle area of visual object itself, so Left and Top can be not 0 and Right-Left and
Bottom-Top are equal to ClientWidth and ClientHeight accordingly).
If You want to get ClientWidth and ClientHeight, first call ClientRect
once to obtain client rectangle and then use Right-Left and Bottom-Top
of that rectangle as ClientWidth and ClientHeight. This is more efficient then calling
ClientRect twice (or more) and using ClientRect.Right-ClientRect.Left and
ClientRect.Bottom-ClientRect.Top . |
- function ControlRect : TRect; virtual;
- This method is intended to return bounding rectangle from topleft corner of
client area of parent window (e.g., form, or parent windowed control). It is convenient to
use it in painting of self-painted (not windowed) controls (derived from XCustomControl),
because in XCL drawing for such controls is taking place on the same canvas belonging to
parent form. |
function ElementAtPos(
X, Y : Integer; IgnoreDisabled : Boolean ) : XVisual;
- Searches visual child element taking place at the given position. Only Visible
childs are taking into consideration, and if IgnoreDisabled parameter is
set to True, then only Enabled childs can be found. If
two or more childs are overlapped, then first found is last created. |
procedure Invalidate;
virtual;
- Invalidates rectangle occupying by visual element in parent window (or its
window if it is Windowed). |
function Windowed:Boolean;
virtual;
- Return True in descendent class if it has its own window. If True, function
GetWindowHandle must return window handle if it exists.
For XCustomControl and its descendents, always returnes False. |
|
Following several functions and procedures made virtual to give possibility to
override it in descenders (and it was useful at least to override
GetBoundsRect/SetBoundsRect in XWindow, and SetColor can be useful for creating combined
controls which use other controls as internal elements). |
function GetEnabled:Boolean; virtual;
procedure SetEnabled(Value:Boolean); virtual;
function GetVisible:Boolean; virtual;
procedure SetVisible(Value:Boolean); virtual;
function GetBoundsRect:TRect; virtual;
procedure SetBoundsRect(Value:TRect); virtual;
procedure SetColor(Value:XColor); virtual;

procedure DoPaint(DC:HDC; Rect:TRect); virtual;
- This is very important procedure. It must paint visual element (form, control)
onto given device context into given rectangle. XVisual implementation just calls
PaintChilds and then PaintErase. If some painting need to be done between these two calls,
override DoPaint and do not call inherited one.
Take in attention, that XCustomControl overrides DoPaint, first calling
PaintChilds and then PaintTo virtual method, so descending self-painting (not windowed)
controls have to use PaintTo to perform painting. |

procedure PaintChilds(DC:HDC; Rect:TRect); virtual;
- Paints childs of visual element. It has to be called before other painting of
visual control itself, because painting of each child is finishing with excluding its area
from clipping area (so it becomes unavailable for further drawing).
Such method of painting allows to prevent flickering in most cases (if
message WM_ERASEBKGND is ignoring - and it is made so in XCL). The main requirement for
self-painting controls is: avoid setting of the pixel more then ones during single paint
operation. And all area, what did not fill during painting must be filled with background
color AFTER painting. |

procedure PaintErase(DC:HDC; Rect:TRect);
- Erases the rest of (non-clipped) Rect area after painting the control and
excludes Rect from clipping region calling ExcludeUpdRect. |

function GetUpdWndRgn:HRgn; virtual;
- Returns handle of updating region for device context, which was obtained (by
XForm) when WM_PAINT message was coming in. Virtual method of XVisual returns 0, and it is
overriding in XForm and XControl. |

procedure ExcludeUpdRect(DC:HDC; Rect:TRect); virtual;
- Excludes rectangle from clip area of given device context. Use this procedure
instead of calling API function ExcludeClipRect, because overridden method of XControl and
XForm additionally excludes Rect from FUpdRgn:HRgn member, allowing to avoid repainting of
other controls not overlapping clipping area (anyway its will not be painted because of
clipping but simultaneous deleting excluded Rects from FUpdRgn speeds up executing). |

procedure ExcludeUpdRgn(DC:HDC; Rgn:HRgn); virtual;
- Similar ExcludeUpdRect above. Use it instead of ExtSelectClipRgn because it does
not only call this API, but also excludes Rgn from FUpdRgn to reduce further painting. |
function GetWindowHandle:HWnd;
virtual;
- Must return window handle for Windowed visual objects (if window is not
allocated, returns 0).
For XCustomControl and its decendents, always returns 0. |
Methods inherited from XClass:
- constructor Create(
AParent : XClass );
- use it to create new XClass instance and to assign AParent to it as parent
XClass object (this will call method AddChild for AParent to
add newly created XClass instance as a child - if AParent is not nil) |
- destructor Destroy;
- destructor first calls DeleteChild method for parent XClass object. |
- procedure AddChild(
Child : XClass ); virtual;
- usually You do not need call AddChild manually. When child created with AParent
parameter, procedure AddChild is called automatically for parent to add newly created
object as a child of it. |
- procedure DeleteChild(
Child : XClass ); virtual;
- also do not call it usually. It is called for parent when child is destroying. |
Event of XCustomControl:
- OnChangeAppearance : XOnEvent;
Tasks.
Use XCustomControl to create self-painted non-windowed controls.
Look at other its descendent to understand how to do it.
Pay attention to implementation of controls, combined from other ones
(XCustomButton, XGroup, etc.) Combining the control from already existing is simplifying
its developing very well. When You combine control from other, derive it from XCustomBevel
and create its internal sub-controls as private fields. In constructor create it with no
parent, assigning OwnerForm to its OwnerForm properties. Also do not forget override
method SetOwnerForm, calling inherited one and reassigning the value to OwnerForm
properties of internal controls.
You may place and size internal controls where You wish. (Changing of
its bounds does not lead to realigning or resizing form and its controls, because its
Parent is nil).
Painting of combined controls usually consist of overriding PaintTo
method, where You have to recall PaintTo methods of internal control, and (may be) paint
something else (e.g., call OnDrawFocusRect event of GlobalFormsManager as it is made for
XButton). At the end of painting self-painted control must erase rest of its rectangle
with background color and exclude it from clipping region (using ExcludeUpdRect or
ExcludeUpdRegion methods). When You erase background after painting of internal controls
(with no parent), You have to perform excluding its rectangle by yourself - in combined
control.
goto XCL page
goto home
page