Sources
Delphi Russian Knowledge Base
DRKB - это самая большая и удобная в использовании база знаний по Дельфи в рунете, составленная Виталием Невзоровым

Как изменить цвет TButton?

01.01.2007
{ 
 
You cannot change the color of a standard TButton,
  since the windows button control always paints itself
with the
  button color
defined in the control panel.
 
But you can derive derive a new component from TButton and handle
  the
and drawing behaviour there.
}
 
 
unit
ColorButton;
 
interface
 
uses
 
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
 
StdCtrls, Buttons, ExtCtrls;
 
type
 
TDrawButtonEvent = procedure(Control: TWinControl;
   
Rect: TRect; State: TOwnerDrawState) of object;
 
 
TColorButton = class(TButton)
 
private
   
FCanvas: TCanvas;
   
IsFocused: Boolean;
   
FOnDrawButton: TDrawButtonEvent;
 
protected
    procedure
CreateParams(var Params: TCreateParams); override;
    procedure
SetButtonStyle(ADefault: Boolean); override;
    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;
    procedure
WMLButtonDblClk(var Message: TWMLButtonDblClk); message WM_LBUTTONDBLCLK;
    procedure
DrawButton(Rect: TRect; State: UINT);
 
public
   
constructor Create(AOwner: TComponent); override;
    destructor
Destroy; override;
    property
Canvas: TCanvas read FCanvas;
  published
    property
OnDrawButton: TDrawButtonEvent read FOnDrawButton write FOnDrawButton;
    property
Color;
 
end;
 
procedure
Register;
 
implementation
 
procedure
Register;
begin
 
RegisterComponents('Samples', [TColorButton]);
end;
 
constructor TColorButton.Create(AOwner: TComponent);
begin
  inherited
Create(AOwner);
 
FCanvas := TCanvas.Create;
end;
 
destructor
TColorButton.Destroy;
begin
  inherited
Destroy;
 
FCanvas.Free;
end;
 
procedure
TColorButton.CreateParams(var Params: TCreateParams);
begin
  inherited
CreateParams(Params);
 
with Params do Style := Style or BS_OWNERDRAW;
end;
 
procedure
TColorButton.SetButtonStyle(ADefault: Boolean);
begin
 
if ADefault <> IsFocused then
 
begin
   
IsFocused := ADefault;
   
Refresh;
 
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.WMLButtonDblClk(var Message: TWMLButtonDblClk);
begin
 
Perform(WM_LBUTTONDOWN, Message.Keys, Longint(Message.Pos));
end;
 
procedure
TColorButton.DrawButton(Rect: TRect; State: UINT);
var
 
Flags, OldMode: Longint;
 
IsDown, IsDefault, IsDisabled: Boolean;
 
OldColor: TColor;
 
OrgRect: TRect;
begin
 
OrgRect := Rect;
 
Flags := DFCS_BUTTONPUSH or DFCS_ADJUSTRECT;
 
IsDown := State and ODS_SELECTED <> 0;
 
IsDefault := State and ODS_FOCUS <> 0;
 
IsDisabled := State and ODS_DISABLED <> 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
   
DrawFrameControl(FCanvas.Handle, Rect, DFC_BUTTON, Flags);
 
 
if IsDown then OffsetRect(Rect, 1, 1);
 
 
OldColor := FCanvas.Brush.Color;
 
FCanvas.Brush.Color := Color;
 
FCanvas.FillRect(Rect);
 
FCanvas.Brush.Color := OldColor;
 
OldMode := SetBkMode(FCanvas.Handle, TRANSPARENT);
 
FCanvas.Font.Color := clBtnText;
 
if IsDisabled then
   
DrawState(FCanvas.Handle, FCanvas.Brush.Handle, nil, Integer(Caption), 0,
   
((Rect.Right - Rect.Left) - FCanvas.TextWidth(Caption)) div 2,
   
((Rect.Bottom - Rect.Top) - FCanvas.TextHeight(Caption)) div 2,
     
0, 0, DST_TEXT or DSS_DISABLED)
 
else
   
DrawText(FCanvas.Handle, PChar(Caption), - 1, Rect,
      DT_SINGLELINE
or DT_CENTER or DT_VCENTER);
 
SetBkMode(FCanvas.Handle, OldMode);
 
 
if Assigned(FOnDrawButton) then
   
FOnDrawButton(Self, Rect, TOwnerDrawState(LongRec(State).Lo));
 
 
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;
end.

В книгах Калверта, Свана и других авторов можно найти похожий текст. Смысл текста – "Изменить цвет кнопок Button, BitBtn нельзя, т.к. их рисует WINDOWS". Если нельзя, но ОЧЕНЬ НУЖНО, то можно.

Небольшой компонент ColorBtn, дает возможность использовать в кнопках цвет. Кроме того, представлено новое свойство - Frame3D, позволяющее получить более реалистичный вид нажатой кнопки. В отличие от API, при изменении значения свойства Frame3D, не требуется переоткрытие компонента.

Примечание. Кнопку по-прежнему рисует WINDOWS, а раскрашивает ее ColorBtn. Код компонента на 90% повторяет код BitBtn, ничего необычного здесь нет. Чаще заглядывайте в VCL - можно найти много интересного. На рисунке представлены ColorButton и ColorBitBtn.

unit colorbtn;
 
interface
 
uses
 
 
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
 
StdCtrls, Buttons;
 
type
 
 
TColorBtn = class(TButton)
 
private
   
FCanvas: TCanvas;
   
IsFocused: Boolean;
    F3DFrame
: boolean;
   
FButtonColor: TColor;
    procedure Set3DFrame
(Value: boolean);
    procedure
SetButtonColor(Value: TColor);
    procedure
CNDrawItem(var Message: TWMDrawItem); message CN_DRAWITEM;
    procedure
WMLButtonDblClk(var Message: TWMLButtonDblClk); message
      WM_LBUTTONDBLCLK
;
    procedure
DrawButtonText(const Caption: string; TRC: TRect; State:
     
TButtonState; BiDiFlags: Longint);
    procedure
CalcuateTextPosition(const Caption: string; var TRC: TRect;
     
BiDiFlags: Longint);
 
protected
    procedure
CreateParams(var Params: TCreateParams); override;
    procedure
SetButtonStyle(ADefault: Boolean); override;
 
public
   
constructor Create(AOwner: TComponent); override;
    destructor
Destroy; override;
  published
    property
ButtonColor: TColor read FButtonColor write SetButtonColor default
      clBtnFace
;
    property Frame3D
: boolean read F3DFrame write Set3DFrame default False;
 
end;
 
procedure
Register;
 
implementation
 
{ TColorBtn }
 
constructor TColorBtn.Create(AOwner: TComponent);
begin
  inherited
Create(AOwner);
 
Height := 21;
 
FCanvas := TCanvas.Create;
 
FButtonColor := clBtnFace;
  F3DFrame
:= False;
end;
 
destructor
TColorBtn.Destroy;
begin
 
FCanvas.Free;
  inherited
Destroy;
end;
 
procedure
TColorBtn.CreateParams(var Params: TCreateParams);
begin
  inherited
CreateParams(Params);
 
with Params do
   
Style := Style or BS_OWNERDRAW;
end;
 
procedure
TColorBtn.Set3DFrame(Value: boolean);
begin
 
if F3DFrame <> Value then
    F3DFrame
:= Value;
end;
 
procedure
TColorBtn.SetButtonColor(Value: TColor);
begin
 
if FButtonColor <> Value then
 
begin
   
FButtonColor := Value;
   
Invalidate;
 
end;
end;
 
procedure
TColorBtn.WMLButtonDblClk(var Message: TWMLButtonDblClk);
begin
 
Perform(WM_LBUTTONDOWN, Message.Keys, Longint(Message.Pos));
end;
 
procedure
TColorBtn.SetButtonStyle(ADefault: Boolean);
begin
 
if IsFocused <> ADefault then
   
IsFocused := ADefault;
end;
 
procedure
TColorBtn.CNDrawItem(var Message: TWMDrawItem);
var
  RC
: TRect;
 
Flags: Longint;
 
State: TButtonState;
 
IsDown, IsDefault: Boolean;
 
DrawItemStruct: TDrawItemStruct;
begin
 
DrawItemStruct := Message.DrawItemStruct^;
 
FCanvas.Handle := DrawItemStruct.HDC;
  RC
:= ClientRect;
 
with DrawItemStruct do
 
begin
   
IsDown := ItemState and ODS_SELECTED <> 0;
   
IsDefault := ItemState and ODS_FOCUS <> 0;
   
if not Enabled then
     
State := bsDisabled
   
else if IsDown then
     
State := bsDown
   
else
     
State := bsUp;
 
end;
 
Flags := DFCS_BUTTONPUSH or DFCS_ADJUSTRECT;
 
if IsDown then
   
Flags := Flags or DFCS_PUSHED;
 
if DrawItemStruct.ItemState and ODS_DISABLED <> 0 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(RC.Left, RC.Top, RC.Right, RC.Bottom);
   
InflateRect(RC, -1, -1);
 
end;
 
if IsDown then
 
begin
   
FCanvas.Pen.Color := clBtnShadow;
   
FCanvas.Pen.Width := 1;
   
FCanvas.Rectangle(RC.Left, RC.Top, RC.Right, RC.Bottom);
   
InflateRect(RC, -1, -1);
   
if F3DFrame then
   
begin
     
FCanvas.Pen.Color := FButtonColor;
     
FCanvas.Pen.Width := 1;
     
DrawFrameControl(DrawItemStruct.HDC, RC, DFC_BUTTON, Flags);
   
end;
 
end
 
else
   
DrawFrameControl(DrawItemStruct.HDC, RC, DFC_BUTTON, Flags);
 
FCanvas.Brush.Color := FButtonColor;
 
FCanvas.FillRect(RC);
 
InflateRect(RC, 1, 1);
 
if IsFocused then
 
begin
    RC
:= ClientRect;
   
InflateRect(RC, -1, -1);
 
end;
 
FCanvas.Font := Self.Font;
 
if IsDown then
   
OffsetRect(RC, 1, 1);
 
DrawButtonText(Caption, RC, State, 0);
 
if IsFocused and IsDefault then
 
begin
    RC
:= ClientRect;
   
InflateRect(RC, -4, -4);
   
FCanvas.Pen.Color := clWindowFrame;
   
Windows.DrawFocusRect(FCanvas.Handle, RC);
 
end;
 
FCanvas.Handle := 0;
end;
 
procedure
TColorBtn.CalcuateTextPosition(const Caption: string; var TRC: TRect;
 
BiDiFlags: Integer);
var
  TB
: TRect;
  TS
, TP: TPoint;
begin
 
with FCanvas do
 
begin
    TB
:= Rect(0, 0, TRC.Right + TRC.Left, TRC.Top + TRC.Bottom);
   
DrawText(Handle, PChar(Caption), Length(Caption), TB, DT_CALCRECT or
     
BiDiFlags);
    TS
:= Point(TB.Right - TB.Left, TB.Bottom - TB.Top);
    TP
.X := ((TRC.Right - TRC.Left) - TS.X + 1) div 2;
    TP
.Y := ((TRC.Bottom - TRC.Top) - TS.Y + 1) div 2;
   
OffsetRect(TB, TP.X + TRC.Left, TP.Y + TRC.Top);
    TRC
:= TB;
 
end;
end;
 
procedure
TColorBtn.DrawButtonText(const Caption: string; TRC: TRect; State:
 
TButtonState; BiDiFlags: Integer);
begin
 
with FCanvas do
 
begin
   
CalcuateTextPosition(Caption, TRC, BiDiFlags);
   
Brush.Style := bsClear;
   
if State = bsDisabled then
   
begin
     
OffsetRect(TRC, 1, 1);
     
Font.Color := clBtnHighlight;
     
DrawText(Handle, PChar(Caption), Length(Caption), TRC,
        DT_CENTER
or DT_VCENTER or BiDiFlags);
     
OffsetRect(TRC, -1, -1);
     
Font.Color := clBtnShadow;
     
DrawText(Handle, PChar(Caption), Length(Caption), TRC,
        DT_CENTER
or DT_VCENTER or BiDiFlags);
   
end
   
else
     
DrawText(Handle, PChar(Caption), Length(Caption), TRC,
        DT_CENTER
or DT_VCENTER or BiDiFlags);
 
end;
end;
 
procedure
Register;
begin
 
RegisterComponents('Controls', [TColorBtn]);
end;
 
end.

Небольшое дополнение. Кнопку по прежнему рисует WINDOWS, а раскрашивает ее ColorBtn. Код компонента на 90% повторяет код BitBtn, ничего необычного здесь нет. Хочется повторить слова Калверта – "Пользуйтесь исходным кодом". Чаще заглядывайте в VCL - можно найти много интересного.

Взято с https://delphiworld.narod.ru


В книгах Калверта, Свана и других авторов можно найти похожий текст. Смысл текста – "Изменить цвет кнопок Button, BitBtn нельзя, т.к. их рисует WINDOWS". Если нельзя, но ОЧЕНЬ НУЖНО, то можно.

Небольшой компонент ColorBtn, дает возможность использовать в кнопках цвет. Кроме того, представлено новое свойство - Frame3D, позволяющее получить более реалистичный вид нажатой кнопки. В отличие от API, при изменении значения свойства Frame3D, не требуется переоткрытие компонента.

Примечание

Кнопку по-прежнему рисует WINDOWS, а раскрашивает ее ColorBtn. Код компонента на 90% повторяет код BitBtn, ничего необычного здесь нет. Чаще заглядывайте в VCL - можно найти много интересного. На рисунке представлены ColorButton и ColorBitBtn.

unit colorbtn;
 
interface
 
uses
 
 
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
 
StdCtrls, Buttons;
 
type
 
 
TColorBtn = class(TButton)
 
private
   
FCanvas: TCanvas;
   
IsFocused: Boolean;
    F3DFrame
: boolean;
   
FButtonColor: TColor;
    procedure Set3DFrame
(Value: boolean);
    procedure
SetButtonColor(Value: TColor);
    procedure
CNDrawItem(var Message: TWMDrawItem); message CN_DRAWITEM;
    procedure
WMLButtonDblClk(var Message: TWMLButtonDblClk); message
      WM_LBUTTONDBLCLK
;
    procedure
DrawButtonText(const Caption: string; TRC: TRect; State:
     
TButtonState; BiDiFlags: Longint);
    procedure
CalcuateTextPosition(const Caption: string; var TRC: TRect;
     
BiDiFlags: Longint);
 
protected
    procedure
CreateParams(var Params: TCreateParams); override;
    procedure
SetButtonStyle(ADefault: Boolean); override;
 
public
   
constructor Create(AOwner: TComponent); override;
    destructor
Destroy; override;
  published
    property
ButtonColor: TColor read FButtonColor write SetButtonColor default
      clBtnFace
;
    property Frame3D
: boolean read F3DFrame write Set3DFrame default False;
 
end;
 
procedure
Register;
 
implementation
 
{ TColorBtn }
 
constructor TColorBtn.Create(AOwner: TComponent);
begin
  inherited
Create(AOwner);
 
Height := 21;
 
FCanvas := TCanvas.Create;
 
FButtonColor := clBtnFace;
  F3DFrame
:= False;
end;
 
destructor
TColorBtn.Destroy;
begin
 
FCanvas.Free;
  inherited
Destroy;
end;
 
procedure
TColorBtn.CreateParams(var Params: TCreateParams);
begin
  inherited
CreateParams(Params);
 
with Params do
   
Style := Style or BS_OWNERDRAW;
end;
 
procedure
TColorBtn.Set3DFrame(Value: boolean);
begin
 
if F3DFrame <> Value then
    F3DFrame
:= Value;
end;
 
procedure
TColorBtn.SetButtonColor(Value: TColor);
begin
 
if FButtonColor <> Value then
 
begin
   
FButtonColor := Value;
   
Invalidate;
 
end;
end;
 
procedure
TColorBtn.WMLButtonDblClk(var Message: TWMLButtonDblClk);
begin
 
Perform(WM_LBUTTONDOWN, Message.Keys, Longint(Message.Pos));
end;
 
procedure
TColorBtn.SetButtonStyle(ADefault: Boolean);
begin
 
if IsFocused <> ADefault then
   
IsFocused := ADefault;
end;
 
procedure
TColorBtn.CNDrawItem(var Message: TWMDrawItem);
var
  RC
: TRect;
 
Flags: Longint;
 
State: TButtonState;
 
IsDown, IsDefault: Boolean;
 
DrawItemStruct: TDrawItemStruct;
begin
 
DrawItemStruct := Message.DrawItemStruct^;
 
FCanvas.Handle := DrawItemStruct.HDC;
  RC
:= ClientRect;
 
with DrawItemStruct do
 
begin
   
IsDown := ItemState and ODS_SELECTED <> 0;
   
IsDefault := ItemState and ODS_FOCUS <> 0;
   
if not Enabled then
     
State := bsDisabled
   
else if IsDown then
     
State := bsDown
   
else
     
State := bsUp;
 
end;
 
Flags := DFCS_BUTTONPUSH or DFCS_ADJUSTRECT;
 
if IsDown then
   
Flags := Flags or DFCS_PUSHED;
 
if DrawItemStruct.ItemState and ODS_DISABLED <> 0 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(RC.Left, RC.Top, RC.Right, RC.Bottom);
   
InflateRect(RC, -1, -1);
 
end;
 
if IsDown then
 
begin
   
FCanvas.Pen.Color := clBtnShadow;
   
FCanvas.Pen.Width := 1;
   
FCanvas.Rectangle(RC.Left, RC.Top, RC.Right, RC.Bottom);
   
InflateRect(RC, -1, -1);
   
if F3DFrame then
   
begin
     
FCanvas.Pen.Color := FButtonColor;
     
FCanvas.Pen.Width := 1;
     
DrawFrameControl(DrawItemStruct.HDC, RC, DFC_BUTTON, Flags);
   
end;
 
end
 
else
   
DrawFrameControl(DrawItemStruct.HDC, RC, DFC_BUTTON, Flags);
 
FCanvas.Brush.Color := FButtonColor;
 
FCanvas.FillRect(RC);
 
InflateRect(RC, 1, 1);
 
if IsFocused then
 
begin
    RC
:= ClientRect;
   
InflateRect(RC, -1, -1);
 
end;
 
FCanvas.Font := Self.Font;
 
if IsDown then
   
OffsetRect(RC, 1, 1);
 
DrawButtonText(Caption, RC, State, 0);
 
if IsFocused and IsDefault then
 
begin
    RC
:= ClientRect;
   
InflateRect(RC, -4, -4);
   
FCanvas.Pen.Color := clWindowFrame;
   
Windows.DrawFocusRect(FCanvas.Handle, RC);
 
end;
 
FCanvas.Handle := 0;
end;
 
procedure
TColorBtn.CalcuateTextPosition(const Caption: string; var TRC: TRect;
 
BiDiFlags: Integer);
var
  TB
: TRect;
  TS
, TP: TPoint;
begin
 
with FCanvas do
 
begin
    TB
:= Rect(0, 0, TRC.Right + TRC.Left, TRC.Top + TRC.Bottom);
   
DrawText(Handle, PChar(Caption), Length(Caption), TB, DT_CALCRECT or
     
BiDiFlags);
    TS
:= Point(TB.Right - TB.Left, TB.Bottom - TB.Top);
    TP
.X := ((TRC.Right - TRC.Left) - TS.X + 1) div 2;
    TP
.Y := ((TRC.Bottom - TRC.Top) - TS.Y + 1) div 2;
   
OffsetRect(TB, TP.X + TRC.Left, TP.Y + TRC.Top);
    TRC
:= TB;
 
end;
end;
 
procedure
TColorBtn.DrawButtonText(const Caption: string; TRC: TRect; State:
 
TButtonState; BiDiFlags: Integer);
begin
 
with FCanvas do
 
begin
   
CalcuateTextPosition(Caption, TRC, BiDiFlags);
   
Brush.Style := bsClear;
   
if State = bsDisabled then
   
begin
     
OffsetRect(TRC, 1, 1);
     
Font.Color := clBtnHighlight;
     
DrawText(Handle, PChar(Caption), Length(Caption), TRC,
        DT_CENTER
or DT_VCENTER or BiDiFlags);
     
OffsetRect(TRC, -1, -1);
     
Font.Color := clBtnShadow;
     
DrawText(Handle, PChar(Caption), Length(Caption), TRC,
        DT_CENTER
or DT_VCENTER or BiDiFlags);
   
end
   
else
     
DrawText(Handle, PChar(Caption), Length(Caption), TRC,
        DT_CENTER
or DT_VCENTER or BiDiFlags);
 
end;
end;
 
procedure
Register;
begin
 
RegisterComponents('Controls', [TColorBtn]);
end;
 
end.
 

Небольшое дополнение. Кнопку по прежнему рисует WINDOWS, а раскрашивает ее ColorBtn. Код компонента на 90% повторяет код BitBtn, ничего необычного здесь нет. Хочется повторить слова Калверта – "Пользуйтесь исходным кодом". Чаще заглядывайте в VCL - можно найти много интересного.

https://delphiworld.narod.ru/

DelphiWorld 6.0