Slider (on/off) style component similar to iPad - delphi

Anyone knows of a On/off Delphi component similar to the slider control found on the iPad?
TAdvSmoothSlider (http://www.tmssoftware.com/site/advsmoothslider.asp) is the only one I found so far, but I would prefer not to have to purchase/install the entire TMS Smooth Controls Pack.
Anyone knows of a similar component? I already have On/Off images in png format so a component based on png images would also work.
Thanks in advance for any replies/ideas.

Could you do with something like this?
unit OnOffSwitch;
interface
uses
Classes, Controls, Windows, Messages, Graphics, Themes;
type
TOnOffSwitch = class(TCustomControl)
private
FMouseHover: Boolean;
FOff: Boolean;
FSliderRect: TRect;
procedure SetMouseHover(Value: Boolean);
procedure SetOff(Value: Boolean);
procedure UpdateSliderRect;
procedure WMWindowPosChanged(var Message: TWMWindowPosChanged);
message WM_WINDOWPOSCHANGED;
procedure CMEnabledChanged(var Message: TMessage);
message CM_ENABLEDCHANGED;
procedure CMFocusChanged(var Message: TCMFocusChanged);
message CM_FOCUSCHANGED;
procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
protected
procedure KeyUp(var Key: Word; Shift: TShiftState); override;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer); override;
procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
procedure Paint; override;
public
constructor Create(AOwner: TComponent); override;
published
property Anchors;
property Constraints;
property DragCursor;
property DragKind;
property DragMode;
property Enabled;
property Font;
property Off: Boolean read FOff write SetOff default True;
property OnClick;
property OnContextPopup;
property OnDblClick;
property OnDragDrop;
property OnDragOver;
property OnEndDock;
property OnEndDrag;
property OnEnter;
property OnExit;
property OnKeyDown;
property OnKeyPress;
property OnKeyUp;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnStartDock;
property OnStartDrag;
property ParentFont default False;
property ParentShowHint;
property PopupMenu;
property ShowHint;
property TabOrder;
property TabStop default True;
property Visible;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TOnOffSwitch]);
end;
{ TOnOffSwitch }
resourcestring
SOff = 'OFF';
SOn = 'ON';
procedure TOnOffSwitch.CMEnabledChanged(var Message: TMessage);
begin
Invalidate;
inherited;
end;
procedure TOnOffSwitch.CMFocusChanged(var Message: TCMFocusChanged);
begin
Invalidate;
inherited;
end;
procedure TOnOffSwitch.CMMouseLeave(var Message: TMessage);
begin
SetMouseHover(False);
inherited;
end;
constructor TOnOffSwitch.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
ControlStyle := [csCaptureMouse, csClickEvents, csDoubleClicks, csOpaque];
FOff := True;
Caption := SOff;
Width := 75;
Height := 25;
TabStop := True;
Font.Name := 'Tahoma';
Font.Style := [fsBold];
end;
procedure TOnOffSwitch.KeyUp(var Key: Word; Shift: TShiftState);
begin
if Key = VK_SPACE then
SetOff(not FOff);
inherited KeyUp(Key, Shift);
end;
procedure TOnOffSwitch.MouseDown(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer);
begin
if (Shift = [ssLeft]) and PtInRect(FSliderRect, Point(X, Y)) then
SetOff(not FOff);
inherited MouseDown(Button, Shift, X, Y);
end;
procedure TOnOffSwitch.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
if GetCaptureControl = nil then
SetMouseHover(PtInRect(FSliderRect, Point(X, Y)));
inherited MouseMove(Shift, X, Y);
end;
procedure TOnOffSwitch.Paint;
var
Button: TThemedButton;
PaintRect: TRect;
Details: TThemedElementDetails;
begin
if ThemeServices.ThemesAvailable then
begin
if not Enabled then
Button := tbPushButtonDisabled
else if not FOff then
Button := tbPushButtonPressed
else
Button := tbPushButtonNormal;
PaintRect := ClientRect;
Details := ThemeServices.GetElementDetails(Button);
ThemeServices.DrawElement(Canvas.Handle, Details, PaintRect);
if FOff then
Inc(PaintRect.Left, Round(2 / 5 * Width))
else
Dec(PaintRect.Right, Round(2 / 5 * Width));
Canvas.Brush.Style := bsClear;
Canvas.Font := Self.Font;
if not Enabled then
Canvas.Font.Color := $00A0A0A0
else
Canvas.Font.Color := $00555555;
DrawText(Canvas.Handle, PChar(Caption), -1, PaintRect, DT_CENTER or
DT_VCENTER or DT_SINGLELINE);
if Enabled and not FOff then
begin
OffsetRect(PaintRect, 0, 1);
Canvas.Font.Color := clWhite;
DrawText(Canvas.Handle, PChar(Caption), -1, PaintRect, DT_CENTER or
DT_VCENTER or DT_SINGLELINE);
end;
if not Enabled then
Button := tbPushButtonDisabled
else if Focused then
Button := tbPushButtonDefaulted
else if FMouseHover then
Button := tbPushButtonHot
else
Button := tbPushButtonNormal;
PaintRect := FSliderRect;
Details := ThemeServices.GetElementDetails(Button);
ThemeServices.DrawElement(Canvas.Handle, Details, PaintRect);
if Focused then
begin
PaintRect := ThemeServices.ContentRect(Canvas.Handle, Details, PaintRect);
SetTextColor(Canvas.Handle, clWhite);
DrawFocusRect(Canvas.Handle, PaintRect);
end;
end;
end;
procedure TOnOffSwitch.SetMouseHover(Value: Boolean);
begin
if FMouseHover <> Value then
begin
FMouseHover := Value;
Invalidate;
end;
end;
procedure TOnOffSwitch.SetOff(Value: Boolean);
begin
if FOff <> Value then
begin
FOff := Value;
if FOff then
Caption := SOff
else
Caption := SOn;
UpdateSliderRect;
Invalidate;
end;
end;
procedure TOnOffSwitch.UpdateSliderRect;
begin
if FOff then
SetRect(FSliderRect, 0, 0, Round(2 / 5 * Width), Height)
else
SetRect(FSliderRect, Round(3 / 5 * Width), 0, Width, Height);
end;
procedure TOnOffSwitch.WMWindowPosChanged(var Message: TWMWindowPosChanged);
begin
inherited;
UpdateSliderRect;
Font.Size := Round(Height div 3) + 1;
end;
end.

AFAIK, TMS Smooth Pack is available to registered Delphi users for free, so you should be able to get it and use its TAdvSmoothSlider in your apps for free too.

Related

How to modernize the look of the TJvCaptionPanel buttons?

I use a TJvCaptionPanel in Delphi 10.4 to show a panel with a caption and buttons:
(TJvCaptionPanel is part of the OpenSource JEDI Visual Component Library available from GetIt)
This is the object code of the JvCaptionPanel1 object instance, so you can paste it in the Form Designer:
object JvCaptionPanel1: TJvCaptionPanel
Left = 560
Top = 79
Width = 210
Height = 306
Align = alRight
Buttons = [capClose, capHelp]
Caption = 'My Test Caption'
CaptionPosition = dpTop
CaptionFont.Charset = DEFAULT_CHARSET
CaptionFont.Color = clWhite
CaptionFont.Height = -13
CaptionFont.Name = 'Tahoma'
CaptionFont.Style = [fsBold]
FlatButtons = True
Icon.Data = {
0000010001001010000001002000680400001600000028000000100000002000
0000010020000000000040040000000000000000000000000000000000000000
00000000000000000000777777A4777777E07777773177777763777777887777
7788777777617777772D777777DF777777A80000000000000000000000000000
00000000000000000000777777C5787878FE7F7F7FFDA9A9A9FDC0C0C0FDBFBF
BFFDA8A8A8FD7F7F7FFD787878FE777777C90000000000000000000000000000
0000000000007676760E777777CBABABABFDF4F4F4FDFDFDFDFDFDFDFDFDFDFD
FDFDFDFDFDFDF4F4F4FDA9A9A9FD777777C77777770C00000000000000000000
000000000000777777A8B0B0B0FDFCFCFCFDFDFDFDFDFDFDFDFDFDFDFDFDFDFD
FDFDFDFDFDFDEADCCEFCF2EAE2FCAEAEAEFD777777A300000000000000000000
000076767635898989FDF9F9F9FDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFD
FDFDDDC4ACFCCDA782FCFCFCFCFCF8F8F8FD888888FD77777730000000000000
00007777778EBEBEBEFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFCFCFCFCD5B5
96FCBE8D5CFCF9F6F3FCFDFDFDFDFDFDFDFDBCBCBCFD77777789000000000000
0000777777BDDBDBDBFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDD7B99BFCB883
4DFCF3EBE3FCFDFDFDFDFDFDFDFDFDFDFDFDD9D9D9FD777777B7000000000000
0000777777C6E0E0E0FDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDC59A6FFCDEC7
AFFCFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDDEDEDEFD777777C1000000000000
0000777777AACFCFCFFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDCAA37BFCE4D0
BDFCFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDCCCCCCFD777777A4000000000000
000077777766A4A4A4FDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDD1AF8DFCE8D8
C8FCFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDA2A2A2FD77777761000000000000
00007372710C797979E7DFDFDFFDFDFDFDFDFDFDFDFDFDFDFDFDDBC2A8FCF0E7
DEFCFDFDFDFDFDFDFDFDFDFDFDFDDDDDDDFD787878E378767509000000000000
0000A47547088376694C848484FBE5E5E5FCFDFDFDFCFDFDFDFCF1E8E0FCFBFA
F9FCFDFDFDFCFDFDFDFCE4E4E4FC838383FA80766C47A2764A08000000000000
0000B1773C9FA7764445827568557B7B7BF1B6B6B6FCE8E8E8FCFCFCFCFCFCFC
FCFCE7E7E7FCB5B5B5FC7B7B7BF07E756B51A476483DB1773CA8000000000000
0000B1773CB7B1773CF5AB77425F8F765C1D7B76718F777676DD7A7A7AFC7A7A
7AFC767676DC7A76728D8C755F1BA8764457B1773DF2B1773CCA000000000000
0000B1763B3CB1773CF5B2783DFEB1773DC2A9774357000000005D5D5D065D5D
5D0600000000A8774552B0773DBCB2783DFEB2783DFCB1773C56000000000000
000000000000B1773B2FB1773CBAB1773CE6B1773C6F00000000000000000000
000000000000B1773C68B1773CEFB1773CD1B1763B470000000000000000E007
0000E0070000C0030000C0030000800100008001000080010000800100008001
0000800100008001000080010000800100008001000082410000C3C30000}
OutlookLook = False
Resizable = False
TabOrder = 2
OnButtonClick = JvCaptionPanel1ButtonClick
end
Although JvCaptionPanel1.FlatButtons = True, as you can see from the above screenshot, the buttons have an old-fashioned "stone-age" "Atari" pixelized look:
This contrasts with the otherwise modern look of my application.
Is it possible to "modernize" the look of the buttons to make them appear more "modern"? How could this be done?
If I were you, I'd create a custom control:
unit PanelCaption;
interface
uses
Windows, Messages, SysUtils, Types, UITypes, Classes, Graphics, Controls,
StdCtrls, Forms;
type
TPanelCaption = class(TCustomControl)
private
FTextColor: TColor;
FCloseBtnHot: Boolean;
FCloseBtnDown: Boolean;
FCloseBtnClicked: TNotifyEvent;
procedure SetTextColor(const Value: TColor);
function CloseBtnRect: TRect;
procedure DoCloseBtnClicked;
protected
procedure Paint; override;
procedure Resize; override;
procedure MouseMove(Shift: TShiftState; X: Integer; Y: Integer); override;
procedure CMTextChanged(var Message: TMessage); message CM_TEXTCHANGED;
procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X: Integer;
Y: Integer); override;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X: Integer;
Y: Integer); override;
public
constructor Create(AOwner: TComponent); override;
published
property Align;
property Anchors;
property BiDiMode;
property Caption;
property Color;
property TextColor: TColor read FTextColor write SetTextColor;
property Constraints;
property Ctl3D;
property DockSite;
property DoubleBuffered;
property DragCursor;
property DragKind;
property DragMode;
property Enabled;
property Font;
property Padding;
property ParentBackground;
property ParentBiDiMode;
property ParentColor;
property ParentCtl3D;
property ParentDoubleBuffered;
property ParentFont default True;
property ParentShowHint;
property PopupMenu;
property ShowHint;
property TabOrder;
property TabStop;
property Touch;
property Visible;
property StyleElements;
property OnAlignInsertBefore;
property OnAlignPosition;
property OnClick;
property OnCloseBtnClick: TNotifyEvent read FCloseBtnClicked write FCloseBtnClicked;
property OnContextPopup;
property OnDblClick;
property OnDragDrop;
property OnDockDrop;
property OnDockOver;
property OnDragOver;
property OnEndDock;
property OnEndDrag;
property OnEnter;
property OnExit;
property OnGesture;
property OnGetSiteInfo;
property OnMouseActivate;
property OnMouseDown;
property OnMouseEnter;
property OnMouseLeave;
property OnMouseMove;
property OnMouseUp;
property OnStartDock;
property OnStartDrag;
property OnUnDock;
end;
procedure Register;
implementation
uses
Math;
function Scale(X: Integer): Integer;
begin
Result := MulDiv(X, Screen.PixelsPerInch, 96);
end;
{ TPanelCaption }
function TPanelCaption.CloseBtnRect: TRect;
begin
Result := Rect(ClientWidth - ClientHeight, 0, ClientWidth, ClientHeight);
end;
procedure TPanelCaption.CMMouseLeave(var Message: TMessage);
begin
if FCloseBtnHot or FCloseBtnDown then
begin
FCloseBtnHot := False;
FCloseBtnDown := False;
InvalidateRect(Handle, CloseBtnRect, False);
end;
end;
procedure TPanelCaption.CMTextChanged(var Message: TMessage);
begin
inherited;
Invalidate;
end;
constructor TPanelCaption.Create(AOwner: TComponent);
begin
inherited;
Color := clActiveCaption;
FTextColor := clCaptionText;
end;
procedure TPanelCaption.DoCloseBtnClicked;
begin
if Assigned(FCloseBtnClicked) then
FCloseBtnClicked(Self);
end;
procedure TPanelCaption.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
Y: Integer);
begin
inherited;
if Button = mbLeft then
begin
FCloseBtnDown := CloseBtnRect.Contains(Point(X, Y));
if FCloseBtnDown then
InvalidateRect(Handle, CloseBtnRect, False);
end;
end;
procedure TPanelCaption.MouseMove(Shift: TShiftState; X, Y: Integer);
var
LCloseButtonHot: Boolean;
begin
LCloseButtonHot := CloseBtnRect.Contains(Point(X, Y));
if LCloseButtonHot <> FCloseBtnHot then
begin
FCloseBtnHot := LCloseButtonHot;
InvalidateRect(Handle, CloseBtnRect, False);
end;
end;
procedure TPanelCaption.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
Y: Integer);
begin
inherited;
if FCloseBtnDown then
begin
FCloseBtnDown := False;
InvalidateRect(Handle, CloseBtnRect, False);
if CloseBtnRect.Contains(Point(X, Y)) then
DoCloseBtnClicked;
end;
end;
procedure GetActualTextHeight(DC: HDC; out H: Integer);
var
m: TTextMetric;
begin
if GetTextMetrics(DC, m) then
H := m.tmHeight - m.tmDescent - m.tmExternalLeading - m.tmInternalLeading
else
H := Scale(20);
end;
procedure TPanelCaption.Paint;
var
R: TRect;
S: string;
XHeight: Integer;
SizeReduction: Integer;
begin
inherited;
Canvas.Brush.Color := Color;
Canvas.Font.Assign(Font);
Canvas.Font.Color := FTextColor;
R := ClientRect;
Dec(R.Right, ClientHeight);
Canvas.FillRect(R);
S := #32 + Caption;
Canvas.TextRect(R, S, [tfSingleLine, tfLeft, tfVerticalCenter, tfEndEllipsis]);
R := CloseBtnRect;
Canvas.Brush.Color := IfThen(FCloseBtnHot, IfThen(FCloseBtnDown, clBlack, clWhite), Color);
Canvas.FillRect(R);
GetActualTextHeight(Canvas.Handle, XHeight);
SizeReduction := R.Height - XHeight;
if SizeReduction > 0 then
R.Inflate(-SizeReduction div 2, -SizeReduction div 2);
Canvas.Pen.Color := IfThen(FCloseBtnHot, IfThen(FCloseBtnDown, clWhite, clBlack), Font.Color);
Canvas.Pen.Width := Scale(2);
Canvas.MoveTo(R.Left, R.Top);
Canvas.LineTo(R.Right, R.Bottom);
Canvas.MoveTo(R.Right, R.Top);
Canvas.LineTo(R.Left, R.Bottom);
end;
procedure TPanelCaption.Resize;
begin
inherited;
Invalidate;
end;
procedure TPanelCaption.SetTextColor(const Value: TColor);
begin
if FTextColor <> Value then
begin
FTextColor := Value;
Invalidate;
end;
end;
procedure Register;
begin
RegisterComponents('Rejbrand 2020', [TPanelCaption]);
end;
end.
Here I chose to implement the close button manually in code. It wouldn't be any more difficult to use a TSpeedButton control instead. In fact, it would be simpler, but then you wouldn't get full control over its appearance and behaviour.

trasparent panel with solid border

Below is a transparent panel vcl
it works.
but I hope to draw solid border(other parts are still transparent)
Is there any hint?
Your comment welcome
unit aframek;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls;
type
TAframekStyle = (
gsBlackness, gsDstInvert, gsMergeCopy, gsMergePaint, gsNotSrcCopy,
gsNotSrcErase, gsPatCopy, gsPatInvert, gsPatPaint, gsSrcAnd,
gsSrcCopy, gsSrcErase, gsSrcInvert, gsSrcPaint, gsWhiteness);
TAframek = class(TCustomControl)
private
FColor: TColor;
FStyle: TAframekStyle;
FOnPaint: TNotifyEvent;
procedure SetColor(Value: TColor);
procedure SetStyle(Value: TAframekStyle);
procedure CMCtl3DChanged(var Message: TMessage); message CM_CTL3DCHANGED;
procedure WMEraseBkgnd(var Message: TMessage); message WM_ERASEBKGND;
procedure WMWindowPosChanging(var Message: TWMWindowPosChanging); message WM_WINDOWPOSCHANGING;
protected
Buffer: TBitmap;
procedure CreateParams(var Params: TCreateParams); override;
procedure Paint; override;
procedure Resize; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property Canvas;
published
property Align;
property Anchors;
property AutoSize;
property BiDiMode;
property BorderWidth;
property Color: TColor read FColor write SetColor;
property Ctl3D;
property Enabled;
property Style: TAframekStyle read FStyle write SetStyle default gsSrcAnd;
property Visible;
property OnClick;
property OnDblClick;
property OnEnter;
property OnExit;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnResize;
property OnPaint: TNotifyEvent read FOnPaint write FOnPaint;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('aka', [TAframek]);
end;
function AframekStyleToInt(gs: TAframekStyle): LongInt;
begin
case gs of
gsBlackness : Result := cmBlackness;
gsDstInvert : Result := cmDstInvert;
gsMergeCopy : Result := cmMergeCopy;
gsMergePaint : Result := cmMergePaint;
gsNotSrcCopy : Result := cmNotSrcCopy;
gsNotSrcErase: Result := cmNotSrcErase;
gsPatCopy : Result := cmPatCopy;
gsPatInvert : Result := cmPatInvert;
gsPatPaint : Result := cmPatPaint;
gsSrcAnd : Result := cmSrcAnd;
gsSrcCopy : Result := cmSrcCopy;
gsSrcErase : Result := cmSrcErase;
gsSrcInvert : Result := cmSrcInvert;
gsSrcPaint : Result := cmSrcPaint;
gsWhiteness : Result := cmWhiteness;
else Assert(True, 'Error parameter in function AframeStyleToInt');
end;
end;
constructor TAframek.Create(AOwner: TComponent);
var
FMarkBrush: LOGBRUSH;
FMarkPen: HPEN;
FPenStyle: array[0..1] of Integer;
FStartAngle: Single;
begin
inherited Create(AOwner);
Buffer := TBitmap.Create;
ControlStyle := [csAcceptsControls, csCaptureMouse, csClickEvents,
csDoubleClicks, csReplicatable];
Width := 100;
Height := 100;
FStyle := gsSrcAnd;
ParentCtl3d := False;
Ctl3D := False;
ParentColor := False;
FColor := clWhite;
end;
destructor TAframek.Destroy;
begin
Buffer.Free;
inherited Destroy;
end;
procedure TAframek.Paint;
var
R: TRect;
rop: LongInt;
begin
R := Rect(0, 0, Width, Height);
Buffer.Width := Width;
Buffer.Height := Height;
Buffer.Canvas.Brush.Style := bsSolid;
Buffer.Canvas.Brush.Color := FColor;
Buffer.Canvas.FillRect(Rect(0, 0, Width, Height));
rop := AframekStyleToInt(FStyle);
StretchBlt(Buffer.Canvas.Handle, 0, 0, Width, Height,
Canvas.Handle, 0, 0, Width, Height, rop);
if Ctl3D then DrawEdge(Buffer.Canvas.Handle, R, BDR_RAISEDINNER, BF_RECT);
Buffer.Canvas.Pen.Mode := pmCopy;
Buffer.Canvas.Pen.Style := psSolid;
Canvas.Draw(0, 0, Buffer);
if Assigned(FOnPaint) then FOnPaint(Self);
end;
procedure TAframek.SetColor(Value: TColor);
begin
if Value <> FColor then
begin
FColor := Value;
RecreateWnd;
end;
end;
procedure TAframek.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.ExStyle := Params.ExStyle + WS_EX_TRANSPARENT;
end;
procedure TAframek.WMWindowPosChanging(var Message: TWMWindowPosChanging);
begin
Invalidate;
inherited;
end;
procedure TAframek.WMEraseBkgnd(var Message: TMessage);
begin
Message.Result := 0;
end;
procedure TAframek.Resize;
begin
Invalidate;
inherited;
end;
procedure TAframek.CMCtl3DChanged(var Message: TMessage);
begin
inherited;
RecreateWnd;
end;
procedure TAframek.SetStyle(Value: TAframekStyle);
begin
if Value <> FStyle then
begin
FStyle := Value;
RecreateWnd;
end;
end;
end.
Looks like you did not draw the border excepting the case Ctrl3D property is True. But it gets False in constructor :)
If you want to draw the border in your own way, try to call TCanvas.Rectangle or TCanvas.FrameRect in Paint method after TCanvas.FillRect.

Change border color of GroupBox in Delphi

How can I change the border color of a GroupBox in Delphi?
Example source code from here:
(*
TEXSColoredGroupBox - Eddie Shipman
TGroupBox that allows colored and flat colored bevels.
Added three properties:
*******************************************************************************
property: - BevelShadowColor-
This is the color of the Bevel's Shadow, Default clBtnShadow.
Change this color to change the color of the Bevel's Shadow.
*******************************************************************************
property: BevelHighlightColor-
This is the color of the Bevel's Highlight, Default clBtnHighlight.
Change this color to change the color of the Bevel's Highlight.
*******************************************************************************
property: BevelWidth-
This is the width of the Bevel.
*******************************************************************************
This component will also draw the frame in "flat mode" if Ctl3D is False
using the BevelShadowColor.
It still has some problems with redrawing controls in design-time but it is
usable...
*******************************************************************************
*)
unit ColoredBorderGroupBox;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, StdCtrls;
type
TEXSColoredGroupBox = class(TGroupBox)
private
{ private declarations }
FBevelLowColor: TColor;
FBevelHiColor : TColor;
FBevelWidth : Integer;
procedure SetBvlLowColor(Value: TColor);
procedure SetBvlHiColor(Value: TColor);
procedure SetBevelWidth(Value: Integer);
procedure CMDialogChar(var Message: TCMDialogChar); message CM_DIALOGCHAR;
procedure CMTextChanged(var Message: TMessage); message CM_TEXTCHANGED;
procedure CMCtl3DChanged(var Message: TMessage); message CM_CTL3DCHANGED;
procedure WMSize(var Message: TMessage); message WM_SIZE;
protected
{ protected declarations }
procedure CreateParams(Var Params:TCreateparams); override;
procedure Paint; override;
procedure SetParent(AParent:TWinControl); override;
procedure AlignControls(AControl: TControl; var Rect: TRect); override;
procedure DrawFrame(Rect:TRect);
procedure DrawFlatFrame(Rect:TRect);
procedure InvalidateFrame;
public
{ public declarations }
Constructor Create(AOwner:TComponent); override;
published
{ published declarations }
property BevelShadowColor :TColor read FBevelLowColor
write SetBvlLowColor;
property BevelHighlightColor:TColor read FBevelHiColor
write SetBvlHiColor;
property BevelWidth :Integer read FBevelWidth
write SetBevelWidth
default 1;
property Align;
property Caption;
property Color;
property Ctl3D;
property DragCursor;
property DragMode;
property Enabled;
property Font;
property ParentColor;
property ParentCtl3D;
property ParentFont;
property ParentShowHint;
property PopupMenu;
property ShowHint;
property TabOrder;
property TabStop;
property Visible;
property OnClick;
property OnDblClick;
property OnDragDrop;
property OnDragOver;
property OnendDrag;
property OnEnter;
property OnExit;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnStartDrag;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('SST', [TEXSColoredGroupBox]);
end;
constructor TEXSColoredGroupBox.Create (AOwner: TComponent);
begin
inherited Create(AOwner);
ControlStyle := [csAcceptsControls, csCaptureMouse, csClickEvents,
csSetCaption, csDoubleClicks, csReplicatable];
FBevelLowColor := clBtnShadow;
FBevelHiColor := clBtnHighlight;
FBevelWidth := 1;
end;
procedure TEXSColoredGroupBox.CreateParams(Var Params:TCreateparams);
begin
inherited CreateParams (Params);
with Params.WindowClass do
Style := Style and not (CS_HREDRAW or CS_VREDRAW);
end;
procedure TEXSColoredGroupBox.Paint;
var
H: Integer;
R: TRect;
begin
// No call to inherited needed.
// inherited Paint;
with Canvas do
begin
Font := Self.Font;
H := TextHeight('0');
R := Rect(0, H div 2 - 1, Width, Height);
if Ctl3D then
begin
DrawFrame(R);
end else
begin
Brush.Color := FBevelLowColor;
DrawFlatFrame(R);
end;
if Text <> '' then
begin
R := Rect(8, 0, 0, H);
Brush.Style := bsClear;
DrawText(Handle, PChar(Text), Length(Text), R, DT_LEFT or DT_SINGLELINE
or
DT_CALCRECT);
// **********************
if not Enabled then
begin
OffsetRect(R, 1, 1);
Canvas.Font.Color := clBtnHighlight;
DrawText(Canvas.Handle, PChar(Text), Length(Text), R, DT_LEFT or
DT_SINGLELINE);
OffsetRect(R, -1, -1);
Canvas.Font.Color := clBtnShadow;
DrawText(Canvas.Handle, PChar(Text), Length(Text), R, DT_LEFT or
DT_SINGLELINE);
end
else
DrawText(Canvas.Handle, PChar(Text), Length(Text), R, DT_LEFT or
DT_SINGLELINE);
// ****************************
// DrawText(Handle, PChar(Text), Length(Text), R, );
end;
end;
end;
procedure TEXSColoredGroupBox.AlignControls(AControl: TControl; var Rect:
TRect);
begin
Canvas.Font := Font;
Inc(Rect.Top, Canvas.TextHeight('0'));
InflateRect(Rect, -1, -1);
if Ctl3d then InflateRect(Rect, -1, -1);
inherited AlignControls(AControl, Rect);
end;
procedure TEXSColoredGroupBox.SetParent(AParent:TWinControl);
begin
inherited SetParent(AParent);
if Parent <> nil then
SetWindowLong(Parent.Handle, GWL_STYLE,
GetWindowLong(Parent.Handle, GWL_STYLE)
and not WS_ClipChildren);
end;
procedure TEXSColoredGroupBox.SetBvlLowColor(Value: TColor);
begin
if FBevelLowColor <> Value then
FBevelLowColor := Value;
InvalidateFrame;
Realign;
end;
procedure TEXSColoredGroupBox.SetBvlHiColor(Value: TColor);
begin
if FBevelHiColor <> Value then
FBevelHiColor := Value;
InvalidateFrame;
Realign;
end;
procedure TEXSColoredGroupBox.DrawFrame(Rect: TRect);
var
CaptionLength: Integer;
begin
with Canvas do
begin
Inc(Rect.Left, FBevelWidth); // 0,5 199,5
Inc(Rect.Top, FBevelWidth); //
Dec(Rect.Right, FBevelWidth); //
Dec(Rect.Bottom, FBevelWidth); // 0,198 198,198
CaptionLength := TextWidth(Text); //
Pen.Color := FBevelHiColor; //
Pen.Width := FBevelWidth; // 1,7 200,7
MoveTo(Rect.Left, Rect.Top); //
LineTo(6,Rect.Top); //
MoveTo(8+CaptionLength+2, Rect.Top); // 1,200 200,200
LineTo(Rect.Right,Rect.Top); //
LineTo(Rect.Right,Rect.Bottom);
LineTo(Rect.Left,Rect.Bottom);
LineTo(Rect.Left, Rect.Top);
Pen.Color := FBevelLowColor;
Pen.Width := FBevelWidth;
OffsetRect(Rect, -FBevelWidth, -FBevelWidth);
MoveTo(Rect.Left, Rect.Top);
LineTo(6,Rect.Top);
MoveTo(8+CaptionLength+2, Rect.Top);
LineTo(Rect.Right,Rect.Top);
LineTo(Rect.Right,Rect.Bottom);
LineTo(Rect.Left,Rect.Bottom);
LineTo(Rect.Left, Rect.Top);
end;
end;
procedure TEXSColoredGroupBox.DrawFlatFrame(Rect: TRect);
var
CaptionLength: Integer;
begin
with Canvas do
begin
Dec(Rect.Right);
Dec(Rect.Bottom);
CaptionLength := TextWidth(Text);
Pen.Color := FBevelLowColor;
Pen.Width := FBevelWidth;
MoveTo(Rect.Left, Rect.Top);
LineTo(6,Rect.Top);
MoveTo(8+CaptionLength+2, Rect.Top);
LineTo(Rect.Right,Rect.Top);
LineTo(Rect.Right,Rect.Bottom);
LineTo(Rect.Left,Rect.Bottom);
LineTo(Rect.Left, Rect.Top);
end;
end;
procedure TEXSColoredGroupBox.CMDialogChar(var Message: TCMDialogChar);
begin
with Message do
if IsAccel(CharCode, Caption) and CanFocus then
begin
SelectFirst;
Result := 1;
end else
inherited;
end;
procedure TEXSColoredGroupBox.CMTextChanged(var Message: TMessage);
begin
inherited;
InvalidateFrame;
Realign;
end;
procedure TEXSColoredGroupBox.CMCtl3DChanged(var Message: TMessage);
begin
inherited;
InvalidateFrame;
Realign;
end;
procedure TEXSColoredGroupBox.WMSize(var Message: TMessage);
begin
if (csDesigning in ComponentState) then
InvalidateFrame;
inherited;
end;
procedure TEXSColoredGroupBox.InvalidateFrame;
var
R: TRect;
begin
R := BoundsRect;
InflateRect(R, 1, 1);
If (Parent <> Nil) and Parent.HandleAllocated then
InvalidateRect(Parent.Handle, #R, True)
Else
inherited Invalidate;
end;
procedure TEXSColoredGroupBox.SetBevelWidth(Value:Integer);
begin
if Value <> FBevelWidth then
FBevelWidth := Value;
Repaint;
end;
end.

Delphi: Changing the Button Color using a Class Helper

I need to change the visual style of my delphi form controls inorder to show them from a .Net environment. To do this, I need to change the colors of delphi controls to blue ($00FCF5EE). I have used "TButton" controls widely which doesn't have a "Color" property.So, instead of changing the buttons to speed buttons, I have tried a different approach by introducing a parent form and inheriting all the other forms from this parent form. In the parent form, I have a class helper to change the color of buttons. Here is the code: (I am using Delphi 2007)
TButtonHelper=class helper for TButton
private
procedure doChangeColor;
public
procedure DrawChangeColor;
end;
TParentForm = class(TForm)
public
procedure AfterConstruction; override;
end;
And in the implementation section, I have
procedure TButtonHelper.doChangeColor;
var
SaveIndex: Integer;
FCanvas:TCanvas;
rect:TRect;
begin
if csDestroying in ComponentState then exit;
FCanvas:=TCanvas.Create;
SaveIndex := SaveDC(Self.Handle);
FCanvas.Lock;
try
FCanvas.Handle := Handle;
FCanvas.Font := Font;
FCanvas.Brush := self.Brush;
FCanvas.Brush.Color:=$00FCF5EE;
FCanvas.FillRect(BoundsRect);//Omitting the code to draw the text
finally
FCanvas.Handle := 0;
FCanvas.Unlock;
RestoreDC(handle, SaveIndex);
FCanvas.Free;
end;
end;
procedure TButtonHelper.DrawChangeColor;
begin
doChangeColor;
self.Repaint;
end;
procedure TParentForm.AfterConstruction;
var
i : Integer;
wc: TControl;
begin
inherited;
for i := 0 to self.ControlCount - 1 do begin
wc:=Controls[i];
if wc is TButton then
TButton(wc).DrawChangeColor;
end;
end;
But this doesn't work. Although the doChangeColor method is executed, it is not changing the color of the button.Please let me know what I am missing here.
Thanking you all,
Pradeep
here's a class that adds color properties to the standard TButton:
unit u_class_colorbutton;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, StdCtrls, Buttons, ExtCtrls;
type
TColorButton = class(TButton)
private
ShowBackColor : Boolean;
FCanvas : TCanvas;
IsFocused : Boolean;
FBackColor : TColor;
FForeColor : TColor;
FHoverColor : TColor;
procedure SetBackColor(const Value: TColor);
procedure SetForeColor(const Value: TColor);
procedure SetHoverColor(const Value: TColor);
protected
procedure CreateParams(var Params: TCreateParams); override;
procedure WndProc(var Message : TMessage); override;
procedure SetButtonStyle(Value: Boolean); override;
procedure DrawButton(Rect: TRect; State: UINT);
procedure CMEnabledChanged(var Message: TMessage); message CM_ENABLEDCHANGED;
procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
procedure CNMeasureItem(var Message: TWMMeasureItem); message CN_MEASUREITEM;
procedure CNDrawItem(var Message: TWMDrawItem); message CN_DRAWITEM;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property BackColor : TColor read FBackColor write SetBackColor default clBtnFace;
property ForeColor : TColor read FForeColor write SetForeColor default clBtnText;
property HoverColor: TColor read FHoverColor write SetHoverColor default clBtnFace;
end;
procedure Register;
implementation
constructor TColorButton.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
ShowBackColor := True;
FCanvas := TCanvas.Create;
BackColor := clBtnFace;
ForeColor := clBtnText;
HoverColor := clBtnFace;
end;
destructor TColorButton.Destroy;
begin
FreeAndNil(FCanvas);
inherited Destroy;
end;
procedure TColorButton.WndProc(var Message : TMessage);
begin
if (Message.Msg = CM_MOUSELEAVE) then
begin
ShowBackColor := True;
Invalidate;
end;
if (Message.Msg = CM_MOUSEENTER) then
begin
ShowBackColor := False;
Invalidate;
end;
inherited;
end;
procedure TColorButton.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
with Params do
Style := Style or BS_OWNERDRAW;
end;
procedure TColorButton.SetButtonStyle(Value: Boolean);
begin
if Value <> IsFocused then
begin
IsFocused := Value;
Invalidate;
end;
end;
procedure TColorButton.CNMeasureItem(var Message: TWMMeasureItem);
begin
with Message.MeasureItemStruct^ do
begin
itemWidth := Width;
itemHeight := Height;
end;
end;
procedure TColorButton.CNDrawItem(var Message: TWMDrawItem);
var
SaveIndex: Integer;
begin
with Message.DrawItemStruct^ do
begin
SaveIndex := SaveDC(hDC);
FCanvas.Lock;
try
FCanvas.Handle := hDC;
FCanvas.Font := Font;
FCanvas.Brush := Brush;
DrawButton(rcItem, itemState);
finally
FCanvas.Handle := 0;
FCanvas.Unlock;
RestoreDC(hDC, SaveIndex);
end;
end;
Message.Result := 1;
end;
procedure TColorButton.CMEnabledChanged(var Message: TMessage);
begin
inherited;
Invalidate;
end;
procedure TColorButton.CMFontChanged(var Message: TMessage);
begin
inherited;
Invalidate;
end;
procedure TColorButton.SetBackColor(const Value: TColor);
begin
if FBackColor <> Value then
begin
FBackColor:= Value;
Invalidate;
end;
end;
procedure TColorButton.SetForeColor(const Value: TColor);
begin
if FForeColor <> Value then
begin
FForeColor:= Value;
Invalidate;
end;
end;
procedure TColorButton.SetHoverColor(const Value: TColor);
begin
if FHoverColor <> Value then
begin
FHoverColor:= Value;
Invalidate;
end;
end;
procedure TColorButton.DrawButton(Rect: TRect; State: UINT);
var Flags, OldMode: Longint;
IsDown, IsDefault, IsDisabled: Boolean;
OldColor: TColor;
OrgRect: TRect;
NewCaption : string;
begin
NewCaption := Caption;
OrgRect := Rect;
Flags := DFCS_BUTTONPUSH or DFCS_ADJUSTRECT;
IsDown := State and ODS_SELECTED <> 0;
IsDisabled := State and ODS_DISABLED <> 0;
IsDefault := State and ODS_FOCUS <> 0;
if IsDown then Flags := Flags or DFCS_PUSHED;
if IsDisabled then Flags := Flags or DFCS_INACTIVE;
if (IsFocused or IsDefault) then
begin
FCanvas.Pen.Color := clWindowFrame;
FCanvas.Pen.Width := 1;
FCanvas.Brush.Style := bsClear;
FCanvas.Rectangle(Rect.Left, Rect.Top, Rect.Right, Rect.Bottom);
InflateRect(Rect, - 1, - 1);
end;
if IsDown then
begin
FCanvas.Pen.Color := clBtnShadow;
FCanvas.Pen.Width := 1;
FCanvas.Brush.Color := clBtnFace;
FCanvas.Rectangle(Rect.Left, Rect.Top, Rect.Right, Rect.Bottom);
InflateRect(Rect, - 1, - 1);
end
else
begin
DrawFrameControl(FCanvas.Handle, Rect, DFC_BUTTON, Flags);
end;
if IsDown then OffsetRect(Rect, 1, 1);
OldColor := FCanvas.Brush.Color;
if ShowBackColor then
FCanvas.Brush.Color := BackColor
else
FCanvas.Brush.Color := HoverColor;
FCanvas.FillRect(Rect);
FCanvas.Brush.Color := OldColor;
OldMode := SetBkMode(FCanvas.Handle, TRANSPARENT);
FCanvas.Font.Color := ForeColor;
if IsDisabled then
DrawState(FCanvas.Handle, FCanvas.Brush.Handle, nil, Integer(NewCaption), 0,
((Rect.Right - Rect.Left) - FCanvas.TextWidth(NewCaption)) div 2,
((Rect.Bottom - Rect.Top) - FCanvas.TextHeight(NewCaption)) div 2,
0, 0, DST_TEXT or DSS_DISABLED)
else
begin
InflateRect(Rect, -4, -4);
DrawText(FCanvas.Handle, PChar(NewCaption), - 1, Rect, DT_WORDBREAK or DT_CENTER);
end;
SetBkMode(FCanvas.Handle, OldMode);
if (IsFocused and IsDefault) then
begin
Rect := OrgRect;
InflateRect(Rect, - 4, - 4);
FCanvas.Pen.Color := clWindowFrame;
FCanvas.Brush.Color := clBtnFace;
DrawFocusRect(FCanvas.Handle, Rect);
end;
end;
procedure Register;
begin
RegisterComponents('Standard', [TColorButton]);
end;
initialization
RegisterClass(TColorButton); // needed for persistence at runtime
end.
You can hack it into your application easily:
find/replace all TButton references to TColorButton
inside your .pas and .dfm files.
You can set separate colors for background, font and hovering.
If you want add styling application wide, maybe it is better to create a GUI with a library that has native support like DevExpress, TMS, ...
Personally, I like DevExpress the most but that's a matter of personal taste.

How to display an "X' in a checked checkbox instead of a checkmark?

The CheckBox component displays a checkmark when checked.
I would like to display an 'X' instead.
You could do something like this:
unit CheckboxEx;
interface
uses
SysUtils, Windows, Messages, Graphics, Classes, Controls, UxTheme;
type
TCrossType = (ctChar, ctGDI);
TCheckboxEx = class(TCustomControl)
private type
THoverState = (hsNormal = 1, hsHover = 2, hsPushed = 3);
private const
DEFAULT_PADDING = 3;
DEFAULT_CHECK_CHAR = '✘';
CHECK_LINE_PADDING = 4;
private
{ Private declarations }
FCaption: TCaption;
FChecked: boolean;
FPadding: integer;
FCheckWidth, FCheckHeight: integer;
FCheckRect, FTextRect: TRect;
theme: HTHEME;
FHoverState: THoverState;
FCheckFont: TFont;
FCheckChar: Char;
FMouseHover: boolean;
FCrossType: TCrossType;
procedure SetCaption(const Caption: TCaption);
procedure SetChecked(Checked: boolean);
procedure SetPadding(Padding: integer);
procedure UpdateMetrics;
procedure CheckFontChange(Sender: TObject);
procedure SetCheckChar(const CheckChar: char);
procedure DetermineState;
procedure SetCrossType(CrossType: TCrossType);
protected
procedure Paint; override;
procedure MouseMove(Shift: TShiftState; X: Integer; Y: Integer); override;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X: Integer;
Y: Integer); override;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X: Integer;
Y: Integer); override;
procedure WndProc(var Message: TMessage); override;
procedure Click; override;
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
procedure KeyUp(var Key: Word; Shift: TShiftState); override;
{ Protected declarations }
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
{ Public declarations }
published
{ Published declarations }
property ParentColor;
property ParentFont;
property Color;
property Visible;
property Enabled;
property TabStop default true;
property TabOrder;
property OnDblClick;
property OnEnter;
property OnExit;
property OnKeyUp;
property OnKeyPress;
property OnKeyDown;
property OnMouseActivate;
property OnMouseLeave;
property OnMouseEnter;
property OnMouseMove;
property OnMouseUp;
property OnMouseDown;
property OnClick;
property Font;
property CheckFont: TFont read FCheckFont write FCheckFont;
property Caption: TCaption read FCaption write SetCaption;
property Checked: boolean read FChecked write SetChecked default false;
property Padding: integer read FPadding write SetPadding default DEFAULT_PADDING;
property CheckChar: Char read FCheckChar write SetCheckChar default DEFAULT_CHECK_CHAR;
property CrossType: TCrossType read FCrossType write SetCrossType default ctGDI;
end;
procedure Register;
implementation
uses Math;
procedure Register;
begin
RegisterComponents('Rejbrand 2009', [TCheckboxEx]);
end;
var
Hit: boolean;
function _EnumFontsProcBool(var LogFont: TLogFont; var TextMetric: TTextMetric;
FontType: Integer; Data: Pointer): Integer; stdcall;
begin
hit := SameStr(LogFont.lfFaceName, Pstring(Data)^);
result := IfThen(hit, 0, 1);
end;
function FontInstalled(const FontName: TFontName): boolean;
var
LF: TLogFont;
fn: string;
begin
hit := false;
FillChar(LF, sizeOf(LF), 0);
LF.lfCharSet := DEFAULT_CHARSET;
fn := FontName;
EnumFontFamiliesEx(GetDC(0), LF, #_EnumFontsProcBool, cardinal(#fn), 0);
result := hit;
end;
function IsKeyDown(const VK: integer): boolean;
begin
IsKeyDown := GetKeyState(VK) and $8000 <> 0;
end;
{ TCheckboxEx }
procedure TCheckboxEx.CheckFontChange(Sender: TObject);
begin
Invalidate;
end;
procedure TCheckboxEx.Click;
begin
inherited;
if Enabled then
begin
SetChecked(not FChecked);
SetFocus;
end;
end;
constructor TCheckboxEx.Create(AOwner: TComponent);
begin
inherited;
TabStop := true;
FMouseHover := false;
FChecked := false;
FPadding := DEFAULT_PADDING;
FCheckChar := DEFAULT_CHECK_CHAR;
FCrossType := ctGDI;
theme := 0;
FHoverState := hsNormal;
FCheckFont := TFont.Create;
FCheckFont.Assign(Font);
if FontInstalled('Arial Unicode MS') then
FCheckFont.Name := 'Arial Unicode MS';
FCheckFont.OnChange := CheckFontChange;
end;
destructor TCheckboxEx.Destroy;
begin
FCheckFont.Free;
if theme <> 0 then
CloseThemeData(theme);
inherited;
end;
procedure TCheckboxEx.DetermineState;
var
OldState: THoverState;
begin
inherited;
OldState := FHoverState;
FHoverState := hsNormal;
if FMouseHover then
FHoverState := hsHover;
if (csLButtonDown in ControlState) or (IsKeyDown(VK_SPACE) and Focused) then
FHoverState := hsPushed;
if (FHoverState <> OldState) and UseThemes then
Invalidate;
end;
procedure TCheckboxEx.KeyDown(var Key: Word; Shift: TShiftState);
begin
inherited;
if Key = VK_SPACE then
DetermineState;
end;
procedure TCheckboxEx.KeyUp(var Key: Word; Shift: TShiftState);
begin
inherited;
if Key = VK_SPACE then
begin
Click;
DetermineState;
end;
end;
procedure TCheckboxEx.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
Y: Integer);
begin
inherited;
DetermineState;
end;
procedure TCheckboxEx.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
inherited;
FMouseHover := true;
DetermineState;
end;
procedure TCheckboxEx.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
Y: Integer);
begin
inherited;
DetermineState;
end;
procedure TCheckboxEx.Paint;
var
ext: TSize;
frect: TRect;
begin
inherited;
Canvas.Brush.Color := Self.Color;
Canvas.FillRect(ClientRect);
if UseThemes then
begin
if theme = 0 then
begin
theme := OpenThemeData(Handle, 'BUTTON');
UpdateMetrics;
end;
if Enabled then
DrawThemeBackground(theme,
Canvas.Handle,
BP_CHECKBOX,
ord(FHoverState),
FCheckRect,
nil)
else
DrawThemeBackground(theme,
Canvas.Handle,
BP_CHECKBOX,
CBS_UNCHECKEDDISABLED,
FCheckRect,
nil);
end
else
if Enabled then
DrawFrameControl(Canvas.Handle,
FCheckRect,
DFC_BUTTON,
DFCS_BUTTONCHECK)
else
DrawFrameControl(Canvas.Handle,
FCheckRect,
DFC_BUTTON,
DFCS_BUTTONCHECK or DFCS_INACTIVE);
Canvas.TextFlags := TRANSPARENT;
Canvas.Brush.Style := bsClear;
Canvas.Font.Assign(Font);
DrawText(Canvas.Handle,
PChar(FCaption),
length(FCaption),
FTextRect,
DT_SINGLELINE or DT_VCENTER or DT_LEFT);
if Focused then
begin
ext := Canvas.TextExtent(FCaption);
frect := Rect(FTextRect.Left,
(ClientHeight - ext.cy) div 2,
FTextRect.Left + ext.cx,
(ClientHeight + ext.cy) div 2);
Canvas.DrawFocusRect(frect);
end;
if FChecked then
case FCrossType of
ctChar:
begin
Canvas.Font.Assign(FCheckFont);
DrawText(Canvas.Handle,
CheckChar,
1,
FCheckRect,
DT_SINGLELINE or DT_VCENTER or DT_CENTER);
end;
ctGDI:
begin
Canvas.Pen.Width := 2;
Canvas.Pen.Color := clBlack;
Canvas.Pen.Mode := pmCopy;
Canvas.MoveTo(FCheckRect.Left + CHECK_LINE_PADDING, FCheckRect.Top + CHECK_LINE_PADDING);
Canvas.LineTo(FCheckRect.Right - CHECK_LINE_PADDING, FCheckRect.Bottom - CHECK_LINE_PADDING);
Canvas.MoveTo(FCheckRect.Right - CHECK_LINE_PADDING, FCheckRect.Top + CHECK_LINE_PADDING);
Canvas.LineTo(FCheckRect.Left + CHECK_LINE_PADDING, FCheckRect.Bottom - CHECK_LINE_PADDING);
end;
end;
end;
procedure TCheckboxEx.SetCaption(const Caption: TCaption);
begin
if not SameStr(FCaption, Caption) then
begin
FCaption := Caption;
Invalidate;
end;
end;
procedure TCheckboxEx.SetCheckChar(const CheckChar: char);
begin
if FCheckChar <> CheckChar then
begin
FCheckChar := CheckChar;
if FChecked then Invalidate;
end;
end;
procedure TCheckboxEx.SetChecked(Checked: boolean);
begin
if FChecked <> Checked then
begin
FChecked := Checked;
Invalidate;
end;
end;
procedure TCheckboxEx.SetCrossType(CrossType: TCrossType);
begin
if FCrossType <> CrossType then
begin
FCrossType := CrossType;
if FChecked then Invalidate;
end;
end;
procedure TCheckboxEx.SetPadding(Padding: integer);
begin
if FPadding <> Padding then
begin
FPadding := Padding;
UpdateMetrics;
Invalidate;
end;
end;
procedure TCheckboxEx.UpdateMetrics;
var
size: TSize;
begin
FCheckWidth := GetSystemMetrics(SM_CXMENUCHECK);
FCheckHeight := GetSystemMetrics(SM_CYMENUCHECK);
if UseThemes then
begin
UxTheme.GetThemePartSize(theme, Canvas.Handle, BP_CHECKBOX, CBS_UNCHECKEDNORMAL, nil, TS_DRAW, size);
FCheckWidth := size.cx;
FCheckHeight := size.cy;
end;
FCheckRect := Rect(0,
(ClientHeight - FCheckHeight) div 2,
FCheckWidth,
(ClientHeight + FCheckHeight) div 2);
FTextRect := Rect(FCheckWidth + FPadding,
0,
ClientWidth,
ClientHeight);
end;
procedure TCheckboxEx.WndProc(var Message: TMessage);
begin
inherited;
case Message.Msg of
CM_MOUSELEAVE:
begin
FMouseHover := false;
DetermineState;
end;
WM_SIZE:
begin
UpdateMetrics;
Invalidate;
end;
WM_SETFOCUS, WM_KILLFOCUS:
Invalidate;
end;
end;
end.
Now (with CrossType set to ctChar) you can use any Unicode character as the checkmark, the default choice being ✘ (U+2718: HEAVY BALLOT X). The images below illustrate that the control works both with and without visual themes:
The following image illustrates that you can choose any character as your checkmark:
This character is ✿ (U+273F: BLACK FLORETTE).
If you set CrossType to ctGDI instead of ctChar, the control will draw a cross manually and not a character:
I didn't use double-buffering this time, because there is no noticable flickering with themes enabled. Without themes, however, there is flickering. To remedy this, simply use a FBuffer: TBitmap and draw on FBuffer.Canvas instead of Self.Canvas and then BitBlt at the end of Paint, as I do in my other controls here at SO.
You'll have to write a custom control and paint it yourself.
If this is a real check box then it's a bad idea to avoid the system's default drawing. However, if you want to do something like a voting form then I could see why you might opt to do this.
I would go the opposite way, anyway, select all items by default and let the user remove the ones who should be left out from the list.
Having checkbutton a serious limitation in designs, who want to stay in VCL, can use BitBtn as a check, using "Kind" property to paint the Cancel or Ok images when user click on it. Also delete after every condition change, the "Caption" property, because the BitBtn must have a square layout to simulate a check. Use also a tLabel at left or right hand as you wish.
if lAutoMode = False then
begin
lAutoMode := True;
BitBtn1.Kind := bkOK;
BitBtn1.Caption := '';
end
else
begin
lAutoMode := False;
BitBtn1.Kind := bkAbort;
BitBtn1.Caption := '';
end;
When create the Form, set the initial state for the BitBtn.

Resources