拡張ボタン(TButtonEX)

 TButtonの拡張ボタンで、TColorbtnと、似ています。
こちらのボタンは、通常のボタン、楕円ボタン、旧タイプフレームボタン、ユーザー描画ボタンがあり、選択して使用します。
ユーザー描画は、描画割り込みを使用し、その割り込みルーチンに、ボタンの作図プログラムを作成することにより自由なボタンを表示することが可能となります。
ボタンを描画するため、TColorbtn と同じように、 TCanvas を追加して、ボタンの描画をします。

拡張ボタン
拡張ボタンデスエブル

 上図で上で、一番左のボタンは、コンポーネント内でなく、描画割り込みで、作図しています。
二番目のボタンは、フォントの色指定をしていますが、ボタンに色をつける場合は、描画割り込みで描画する必要があります。
三番目の楕円ボタンは、楕円形状を指定しています。
一番右は、スタンダードなボタンで、TButtonと同じボタンになります。
下側は、デスエブル状態です。

TButtonEX コンポーネントのリスト

interface

uses
  Windows, SysUtils, Classes, Controls, StdCtrls, Graphics, Messages;

type
  TDrawButtonEvent = procedure (Control: TWinControl; Rect: TRect; Caption: TCaption;
  State: TOwnerDrawState) of object;
  TButtonExStyle = (bsStandard, bsOvalDraw, bsOwnerDraw, bsOwnerDrawWithFrame);

  TButtonEX = class(TButton)
  private
    { Private 宣言 }
    FBtnColor : TColor;
    FCanvas: TCanvas;
    FIsFocused: Boolean;
    FStyle: TButtonExStyle;
    FOnDrawButton: TDrawButtonEvent;
    procedure SetStyle(Value: TButtonExStyle);
    procedure CNMeasureItem(var Msg: TWMMeasureItem); message CN_MEASUREITEM;
    procedure CNDrawItem(var Msg: TWMDrawItem); message CN_DRAWITEM;
    procedure SetBtnColor(Value: TColor);
  protected
    { Protected 宣言 }
    procedure CreateParams(var Params: TCreateParams); override;
    procedure DrawButton(const Rect: TRect; State: TOwnerDrawState);
  dynamic;
    procedure SetButtonStyle(ADefault: Boolean); override;
    procedure WMLButtonDblClk(var Message: TWMLButtonDblClk); message WM_LBUTTONDBLCLK;
  public
    { Public 宣言 }
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    property Canvas: TCanvas read FCanvas;
  published
    { Published 宣言 }
    property ButtonStyle: TButtonExStyle read FStyle write SetStyle
                           default bsStandard;
    property OnDrawButton: TDrawButtonEvent read FOnDrawButton
                           write FOnDrawButton;
    property Color;
    property ParentColor;
    property BtnColor: Tcolor read FBtnColor write SetBtnColor default clBtnFace;
  end;

procedure Register;

implementation

uses Buttons;

// コンポーネントの登録
procedure Register;
begin
  RegisterComponents('MyVCL', [TButtonEX]);
end;

// TButtonEXの生成
constructor TButtonEx.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);          	// Button 生成
  FCanvas := TControlCanvas.Create;   	// Canvas 生成
  TControlCanvas(FCanvas).Control := Self;
  FBtnColor := clBtnFace;
end;

// TButtonの破棄
destructor TButtonEx.Destroy;
begin
  FCanvas.Free; 		// Canvasの解放
  inherited Destroy; 	// Buttonの破棄
end;

// パラメーターの生成
procedure TButtonEx.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  if FStyle <> bsStandard then
    Params.Style := Params.Style or BS_OWNERDRAW
  else
    CreateSubClass(Params, 'BUTTON');
end;

// ダブルクリック時、ボタンダウンに変換
procedure TButtonEx.WMLButtonDblClk(var Message: TWMLButtonDblClk);
begin
  Perform(WM_LBUTTONDOWN, Message.Keys, Longint(Message.Pos));
end;

// ボタンカラーのセット
procedure TButtonEx.SetBtnColor(Value: TColor);
begin
  if FBtnColor <> Value then
    begin
      FBtnColor := Value;
      Refresh;
    end;
end;

// ボタンスタイルのセットされたらボタン再生成
procedure TButtonEx.SetStyle(Value: TButtonExStyle);
begin
  if FStyle <> Value then
    begin
      FStyle := Value;
      RecreateWnd;
    end;
end;

// ボタンスタイル指定によるボタンの描画
procedure TButtonEx.DrawButton(const Rect: TRect; State: TOwnerDrawState);
var
  R, DR: TRect;
begin
  R := Rect;
  // フレーム付オーナー作図ボタンなら
  if FStyle = bsOwnerDrawWithFrame then
  begin
    DR := R;
    if odFocused in State then
    begin
      Inc(DR.Left, 1);
      Inc(DR.Top, 1);
      Dec(DR.Right, 1);
      Dec(DR.Bottom, 1);
    end;
    if not (odSelected in State) then
        DrawButtonFace(FCanvas, DR, 1, bsAutoDetect, False, odSelected in State, FIsFocused)
    else
    begin
      FCanvas.Pen.Color := clBtnShadow;
      FCanvas.Pen.Width := 1;
      FCanvas.Brush.Color := clBtnFace;
      FCanvas.Rectangle(DR.Left, DR.Top, DR.Right, DR.Bottom);
    end;
    DR := R;
    Inc(DR.Left, 3);
    Inc(DR.Top, 3);
    Dec(DR.Right, 3);
    Dec(DR.Bottom, 3);
    if Assigned(FOnDrawButton) then
      OnDrawButton(Self, DR, Caption, State)
    else
    begin
      if Enabled then
          DrawText(FCanvas.Handle, PChar(Caption), -1, DR, DT_SINGLELINE or DT_CENTER or DT_VCENTER)
      else
        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)
    end;
    InflateRect(DR, -1, -1);
    if odFocused in State then
    begin
      FCanvas.Pen.Color := clWindowFrame;
      FCanvas.Font.Color := clBlack;
      FCanvas.Brush.Color := clBtnFace;
      DrawFocusRect(FCanvas.Handle, DR);
      FCanvas.Brush.Style := BsClear;
      FCanvas.Rectangle(R.Left, R.Top, R.Right, R.Bottom);
    end;
  end
    // オーナー作図ボタンで描画割り込みが設定されていなかったら
  else if (FStyle = bsOwnerDraw) and not Assigned(FOnDrawButton) then
  begin
    FCanvas.Brush.Color := FBtncolor;
    FCanvas.FillRect(R);
  end
    // 楕円ボタンだったら
  else if FStyle = bsOvalDraw then
    with FCanvas do
    begin
      DR := R;
      Dec(DR.Right, 1);
      Dec(DR.Bottom, 1);
      Pen.Style := psClear;
      if odSelected in State then
        Brush.Color := clBtnShadow
      else
        Brush.Color := clBtnHighlight;
      with R do
        Pie(Left, Top, Right, Bottom, Right, Top, Left, Bottom);

      if odSelected in State then
        Brush.Color := clBtnHighlight
      else
        Brush.Color := clBtnShadow;
      with R do
        Pie(Left, Top, Right, Bottom, Left, Bottom, Right, Top);

      Pen.Color := clWindowFrame;
      if odFocused in State then
      begin
        Pen.Style := psSolid;
        Brush.Style := BsClear;
        with DR do
          Ellipse(Left, Top, Right, Bottom);
      end;

      Brush.Color := FBtnColor;
      Brush.Style := BsSolid;
      if not (odSelected in State) then
      begin
        Pen.Style := psClear;
        Inc(DR.Left, 2);
        Inc(DR.Top, 2);
        Dec(DR.Right, 2);
        Dec(DR.Bottom, 2);
      end
      else
      begin
        Pen.Style := psSolid;
        Inc(DR.Left, 3);
        Inc(DR.Top, 3);
        Dec(DR.Right, 2);
        Dec(DR.Bottom, 2);
      end;
      with DR do
        Ellipse(Left, Top, Right, Bottom);
    end;
    if odDisabled in State then FCanvas.Font.Color := clGrayText
    else
      FCanvas.Font.Color := Font.Color;
    // オーナー作図ボタンで描画割り込みが設定されていたら割り込み処理
    if Assigned(FOnDrawButton) and (FStyle = bsOwnerDraw) then
      FOnDrawButton(Self, R, Caption, State)
    else
      // フレーム付オーナー作図ボタンでなかったら
      if FStyle <> bsOwnerDrawWithFrame then
      begin
        DR := R;
        Dec(DR.Right, 2);
        Dec(DR.Bottom, 2);
        DrawText(FCanvas.Handle, PChar(Caption), -1, DR,
                 DT_EXPANDTABS or DT_WORDBREAK or DT_CALCRECT);
                 InflateRect(R, (DR.Right - R.Right) div 2,
                 (DR.Bottom - R.Bottom) div 2);
        DrawText(FCanvas.Handle, PChar(Caption), -1, R,
        DT_EXPANDTABS or DT_WORDBREAK);
      end;
end;

// ボタンスタイルのセット
procedure TButtonEx.SetButtonStyle(ADefault: Boolean);
const
  BS_MASK = $000F;
var
  Style: Word;
begin
  if HandleAllocated then
    if FStyle = bsStandard then
    begin
      if ADefault then Style := BS_DEFPUSHBUTTON else Style := BS_PUSHBUTTON;
      if GetWindowLong(Handle, GWL_STYLE) and BS_MASK <> Style then
        SendMessage(Handle, BM_SETSTYLE, Style, 1);
    end
    else
    if FIsFocused <> ADefault then
    begin
      FIsFocused := ADefault;
      Refresh;
    end;
end;

// アイテムのサイズ設定
procedure TButtonEx.CNMeasureItem(var Msg: TWMMeasureItem);
begin
  with Msg.MeasureItemStruct^ do
  begin
    itemWidth := Width;
    itemHeight := Height;
  end;
end;

// アイテムの作図
procedure TButtonEx.CNDrawItem(var Msg: TWMDrawItem);
var
  SaveIndex: Integer;
  State: TOwnerDrawState;
begin
  with Msg.DrawItemStruct^ do
  begin
    State := TOwnerDrawState(LongRec(itemState).Lo);
    SaveIndex := SaveDC(hDC);
    FCanvas.Lock;
    try
      FCanvas.Handle := hDC;
      FCanvas.Font := Font;
      FCanvas.Brush.Color := clBtnFace;
      FCanvas.Brush.Style := bsSolid;
      FCanvas.Pen.Mode := PmCopy;
      DrawButton(rcItem, State);
    finally
      FCanvas.Handle := 0;
      FCanvas.Unlock;
      RestoreDC(hDC, SaveIndex);
    end;
  end;
  Msg.Result := 1;
end;


end.

TButtonEXの使用例

 
 フォームに、TButtonEX を四個作成します。
ボタンの配置

 最初のボタンは、プロパティのButtonStyle をbsOwnerDraw に設定した場合です。
更に、イベントのOnDrawButton を生成します、そして、ButtonEX1DrawButtonの中に描画プログラムを記述します。

二番目のボタンは、ButtonStyle を bsOwnerDrawWithFrame に設定します。
フォントの色を追加で設定しています。
bsOwnerDrawWithFrameの場合、フォントの色は指定できますが、ボタンの色は指定しても無効となります。
ボタンに色を付ける場合は場合は、OnDrawButton割り込みで作図をする必要があります。
その場合、Caption の文字も作図する必要があります。

三番目のボタンは、ButtonStyleを bsOvalDraw にした場合で、フォントの色と、ボタンの色を指定しています。

四番目のボタンは、ButtonStyleを bsStandard にした場合で、Windowsのデフォルトのボタンが表示されます。

 unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, ButtonEX;

type
  TForm1 = class(TForm)
  ButtonEX1: TButtonEX;
  ButtonEX2: TButtonEX;
  ButtonEX3: TButtonEX;
  ButtonEX4: TButtonEX;
  procedure ButtonEX1DrawButton(Control: TWinControl; Rect: TRect; Caption: TCaption; State: TOwnerDrawState);
  procedure ButtonEX1MouseEnter(Sender: TObject);
  procedure ButtonEX1MouseLeave(Sender: TObject);
  procedure ButtonEX1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  procedure ButtonEX1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  procedure ButtonEX2Click(Sender: TObject);
  procedure ButtonEX4Click(Sender: TObject);
  procedure ButtonEX3Click(Sender: TObject);
  procedure ButtonEX1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}
var
  FButtonF : Byte; // ButtonEX1のボタンの状態

// ButtonEX1の作図
procedure TForm1.ButtonEX1DrawButton(Control: TWinControl; Rect: TRect; Caption: TCaption; State: TOwnerDrawState);
var
  R, G, B : short; 		// ボタンの明度調整用
  ButtonColor : TColor;
  fontColor : TColor;
begin
  ButtonColor := ClGreen; 		// ボタンの色設定
  R:= GetRValue(ButtonColor); 	// RGBに変換
  G:= GetGValue(ButtonColor);
  B:= GetBValue(ButtonColor);
  if FButtonF = 0 then 		// 通常
    ButtonColor := RGB(R, G, B);
  if FButtonF = 1 then begin 	// マウスがボタンの上に来たときの色
    R := R + 48;
    if R > 255 then R := 255;
    G := G + 48;
    if G > 255 then G := 255;
    B := B + 48;
    if B > 255 then B := 255;
    ButtonColor := RGB(R, G, B);
  end;
  if FButtonF = 2 then begin 	// ボタンが押された時の色
    R := round(R / 1.2);
    G := round(G / 1.2);
    B := round(B / 1.2);
    ButtonColor := RGB(R, G, B);
  end;
  // ボタンの明度によりフォントの色設定
  if R + G + B > 400 then fontColor := ClBlack
                     else fontColor := ClWhite;
  // ボタンの描画
  with TButtonEx(Control) do begin
    Canvas.Brush.Style := bsSolid;
    Canvas.Pen.Color := clBlack;
    Canvas.Pen.Style := psSolid;
    Canvas.pen.Width := 1;
    Canvas.Font.Color := fontColor;
    if Enabled then begin 		// イネーブルだったら
      Canvas.Brush.Color := ButtonColor; 	// 設定色
    end
    else 				// デスエブルだったらダーク色
    begin
      R := R div 2;
      G := G div 2;
      B := B div 2;
      ButtonColor := RGB(R, G, B);
      Canvas.Brush.Color := ButtonColor;
    end;
    Canvas.RoundRect(Rect.Left, Rect.Top, Rect.Right, Rect.Bottom, 2, 2);
    // 文字の作図
    if Enabled then 			// イネーブル文字作図
      DrawText(Canvas.Handle, PChar(Caption), -1, RECT, DT_SINGLELINE or DT_CENTER or DT_VCENTER)
    else 				// デスエブル文字作図
      DrawState( Canvas.Handle, Canvas.Brush.Handle, nil, Integer(Caption), 0,
               ((Rect.Right - Rect.Left) - Canvas.TextWidth(Caption)) div 2,
               ((Rect.Bottom - Rect.Top) - Canvas.TextHeight(Caption)) div 2,
                 0, 0, DST_TEXT or DSS_DISABLED)
  end;
end;

// ButtonEX1でマウスを左ボタンを押した場合
procedure TForm1.ButtonEX1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  FButtonF := 2;
  ButtonEX1.Repaint;
end;

// ButtonEX1の上にマウスカーソルが入った場合
procedure TForm1.ButtonEX1MouseEnter(Sender: TObject);
begin
  FButtonF := 1;
  ButtonEX1.Repaint;
end;

// ButtonEX1の上からマウスカーソルが離れた場合
procedure TForm1.ButtonEX1MouseLeave(Sender: TObject);
begin
  FButtonF := 0;
  ButtonEX1.Repaint;
end;

// ButtonEX1でマウスの左ボタンをあげた場合
procedure TForm1.ButtonEX1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  FButtonF := 1;
  ButtonEX1.Repaint;
end;

procedure TForm1.ButtonEX1Click(Sender: TObject);
begin
  if ButtonEX4.Enabled then ButtonEX4.Enabled := False
                       else ButtonEX4.Enabled := True;
end;

procedure TForm1.ButtonEX2Click(Sender: TObject);
begin
  if ButtonEX1.Enabled then ButtonEX1.Enabled := False
                       else ButtonEX1.Enabled := True;
end;

procedure TForm1.ButtonEX3Click(Sender: TObject);
begin
  if ButtonEX2.Enabled then ButtonEX2.Enabled := False
                       else ButtonEX2.Enabled := True;
end;

procedure TForm1.ButtonEX4Click(Sender: TObject);
begin
  if ButtonEX3.Enabled then ButtonEX3.Enabled := False
                       else ButtonEX3.Enabled := True;
end;

end.

 

  プロパティのButtonStyle をbsOwnerDraw にした場合 ボタンを作図に、ボタンの状態を取得するため TOwnerDrawStateを使用していますが、マウスカーソルの状態を取得する事が出来ません。
その為、ボタンの上にマウスが来たのか、離れたのか分からないので、MouseEnter、MouseLeave、MouseDown、MouseUp 割り込みを使用して、状態フラグ(FButtonF)を使用して、再描画することにより、ボタンの色を変更しています。
TColorBtn では、マウスカーソルの割り込みをコンポーネントの中に、組み込んでいますが、TButtonExも、コンポーネントの中に組み込んだ方が良いかも知れません。
 TColorBtn と、TButtonEX のコンポーネントを参考にすれば、ボタンがどの様にして描画されるか分かると思います。


    download ButtonEX.zip


     コンポーネント一覧へ戻る