I'm trying to make my own component that will be themed the same as the rest of application (the theme is set in Project > Options > Application > Appearance
).
The control is derived from TWinControl
(in the red frame below). How can I apply the application theme to my component?
I will use on it a lot of stanard controls, like buttons, edits and so on.
I tried to find something in Google, but maybe my English is the problem with asking the right question :)
There is no special skin data specified for your component, so you need to pick parts of other similar components from VCL which look like yours. Then you need to look at the source code of this component and implement drawing the same with your specific changes. You didn't provide a detailed description of your component, so it all up to our fantasy. Let's say: you want to have something like TPanel with a custom tab in red rectangle in the middle of it. We will choose as parent TCustomControl (not TWinControl), because there we have implemented Canvas for our custom drawing and theme support. We will override UpdateStyleElements to react to themes changing and Paint for our drawing (TCustomPanel does the same, we take part of its paint function).
unit Component1;
interface
uses
System.SysUtils, System.Classes, vcl.Controls, vcl.Styles, WinApi.Windows,
vcl.Themes, Vcl.Graphics, Vcl.ExtCtrls;
type
TComponent1 = class(TCustomControl)
private
protected
procedure Paint; override;
procedure UpdateStyleElements; override;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TComponent1]);
end;
{ TComponent1 }
procedure TComponent1.Paint;
const
Alignments: array[TAlignment] of Longint = (DT_LEFT, DT_RIGHT, DT_CENTER);
VerticalAlignments: array[TVerticalAlignment] of Longint = (DT_TOP, DT_BOTTOM, DT_VCENTER);
var
Rect: TRect;
LColor: TColor;
LStyle: TCustomStyleServices;
LDetails: TThemedElementDetails;
TopColor, BottomColor: TColor;
BaseColor, BaseTopColor, BaseBottomColor: TColor;
Flags: Longint;
procedure AdjustColors(Bevel: TPanelBevel);
begin
TopColor := BaseTopColor;
if Bevel = bvLowered then
TopColor := BaseBottomColor;
BottomColor := BaseBottomColor;
if Bevel = bvLowered then
BottomColor := BaseTopColor;
end;
begin
//get rect, where we will drawing
Rect := GetClientRect;
//initilize colors
BaseColor := Color;
BaseTopColor := clBtnHighlight;
BaseBottomColor := clBtnShadow;
//get style
LStyle := StyleServices(Self);
if LStyle.Enabled and (seClient in StyleElements) then
begin
//get detail(background) of our style, which we will use
LDetails := LStyle.GetElementDetails(tpPanelBackground);
//check, if in this style our color is changed - we take it
if LStyle.GetElementColor(LDetails, ecFillColor, LColor) and (LColor <> clNone) then
BaseColor := LColor;
//get detail(border) of our style, which we will use
LDetails := LStyle.GetElementDetails(tpPanelBevel);
//check, if in this style our color is changed - we take it
if LStyle.GetElementColor(LDetails, ecEdgeHighLightColor, LColor) and (LColor <> clNone) then
BaseTopColor := LColor;
if LStyle.GetElementColor(LDetails, ecEdgeShadowColor, LColor) and (LColor <> clNone) then
BaseBottomColor := LColor;
end;
//draw top border
if BevelOuter <> bvNone then
begin
AdjustColors(BevelOuter);
Frame3D(Canvas, Rect, TopColor, BottomColor, BevelWidth);
end;
//if style does not draw borders - do it by ourselves
if not (LStyle.Enabled and (csParentBackground in ControlStyle)) then
Frame3D(Canvas, Rect, BaseColor, BaseColor, BorderWidth)
else
InflateRect(Rect, -Integer(BorderWidth), -Integer(BorderWidth));
if BevelInner <> bvNone then
begin
AdjustColors(BevelInner);
Frame3D(Canvas, Rect, TopColor, BottomColor, BevelWidth);
end;
with Canvas do
begin
if not LStyle.Enabled or not ParentBackground or not (seClient in StyleElements) or
(not LStyle.IsSystemStyle and (Parent <> nil) and (Parent is TCustomPanel) and
TCustomPanel(Parent).DoubleBuffered {and not CheckParentBackground(Parent)})
then
begin
//set curect brush color
Brush.Color := BaseColor;
//and fill all client rect with it
FillRect(Rect);
end;
//drawing red rectangle
Brush.Style := bsClear;
Pen.Color := clRed;
InflateRect(Rect, -30, -30);
Rectangle(Rect);
if LStyle.Enabled then begin
//draw
//make tab smaller
InflateRect(Rect, -10, -10);
//move tab to bottom of recrangle
OffsetRect(Rect, 0, 10 - 1);
//get slyled tab
LDetails := LStyle.GetElementDetails(ttTabItemSelected);
//draw tab
LStyle.DrawElement(Handle, LDetails, rect);
//draw some text on tab
Brush.Style := bsClear;
Font := Self.Font;
Flags := DT_EXPANDTABS or DT_SINGLELINE or
VerticalAlignments[taVerticalCenter] or Alignments[taCenter];
Flags := DrawTextBiDiModeFlags(Flags);
if LStyle.Enabled and (seFont in StyleElements) then
begin
LDetails := LStyle.GetElementDetails(tpPanelBackground);
if not LStyle.GetElementColor(LDetails, ecTextColor, LColor) or (LColor = clNone) then
LColor := Font.Color;
LStyle.DrawText(Handle, LDetails, 'CustomCaption', Rect, TTextFormatFlags(Flags), LColor)
end
else
DrawText(Handle, Caption, -1, Rect, Flags);
end;
end;
end;
procedure TComponent1.UpdateStyleElements;
begin
inherited;
end;
end.
Another standard part of styles you can find in module ‘Vcl.Themes’. Sources are simplified, but you can start from this point.
P.S. If your component got more advanced drawing – you may use a style hook same as other advanced components. Look at ‘class constructor’ and ‘class destructor’.