Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2014年11月17日] [更新 2014年12月01日] Delphi サンプルプログラム集
   
166_エディットコントロールにボタンを配置
動作確認等 Windows 7 U64(SP1) + Delphi XE(UP1) Pro
166_ButtonedEdit.zip [3,879 KB] 2014年12月01日版(EXE同梱)



  • 2012年12月01日
  • エディットコントロールに,入力できない右マージンの設定コードを追加




ボタン付きエディットコントロール


エディットコントロールにボタンを配置するサンプルです.ボタン付きのエディットコントロールということになります.
本ページのサンプルコードと同じような手法を使用すると,TImage を配置して,画像表示機能付きのエディットコントロールも作成できます.このように,コントロール内に配置したコントロールのことをインプレイスコントロールと言うことがあります.

[164] [03_TCalendar TMaskEdit で和暦表示] のサンプルでは,エディットコントロールにボタンを配置しました.このボタンを配置する部分だけの参考サンプルとなっています.
エディットコントロールに,他のコントロールを配置すると,エディットコントロールにフォーカスを移動したり,入力作業を行うと,ボタンの再描画が行われます.そのため,ボタンが正常に表示されません.その対応が必要となります.
また,エディットコントロール内にボタンを配置すると,その部分に入力したテキストは見えなくなります.マージン等の設定が必要となりますが,TMaskEdit の場合は,入力可能な位置と桁数が決まっています.その処理は必要ありません.

本ページのサンプルは,こんな方法でエディットコントロールに,他のコントロールを配置できるという基本的な方法です.汎用的にするには,どんなプロパティやメソッド,あるいはどのようなイベントを実装すべきか検討し,それらのコードを実装する必要があります.



[備考]
Delphi 2009 以降には,エディットコントロールに,ボタンの機能を追加できる TButtonedEdit コンポーネントがあります.







01_エディットコントロール内にボタンを配置  -  TBitBtn


ウィンドウハンドルを持つボタン,TBitBtn の場合のサンプルです.
エディットコントロールの Style に WS_CLIPCHILDREN を追加して,親コントロールの再描画の際に子コントロールを再描画の対象外にしています.




図1
再描画の対応コードがある場合
図2
再描画の対応コードがない場合

リスト1
テストに使用したコード
これ以降のサンプルでも,生成するクラス名だけが違うだけで,後は全て同じ
var
  EditEx : TplEditBitBtn;

procedure TForm1.FormCreate(Sender: TObject);
begin
  EditEx        := TplEditBitBtn.Create(Self);
  EditEx.Parent := Self;
  EditEx.Top    := 40;
  EditEx.Left   := 20;
end;

procedure TForm1.FormShow(Sender: TObject);
begin
  EditEx.SetFocus;
end;


リスト2
TBitBtn を配置するエディットコントロールのクラス
unit plEditTBitBtn;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Controls, Dialogs,
  StdCtrls, Buttons, Mask;

type
  TplEditBitBtn = class(TCustomMaskEdit)
  private
    FButton: TBitBtn;
    FOnButtonClick: TNotifyEvent;
    procedure WMSize(var Message: TWMSize); message WM_SIZE;
  protected
    procedure ButtonClick (Sender: TObject);
    procedure CreateParams(var Params: TCreateParams); override;
  public
    constructor Create(AOwner: TComponent); override;
  published
    property OnButtonClick: TNotifyEvent read FOnButtonClick write FOnButtonClick;
  end;

implementation

{ TplEditBitBtn }

//-----------------------------------------------------------------------------
//  ボタン付きエディットのCreate処理
//-----------------------------------------------------------------------------
constructor TplEditBitBtn.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  AutoSelect := False;
  Width      := 160;
  EditMask   := '!0000年 00月 00\日;1; ';
  EditText   := FormatDateTime('yyyy年 MM月 dd日', Now);

  //ボタンを作成する
  FButton := TBitBtn.Create (Self);
  with FButton do begin
    Parent  := Self;
    Width   := 18;
    OnClick := ButtonClick;
    //Glyphの設定が必要であれば,ここでリソースから読込む
    //あるいは,Glyph設定用のプロパティを実装する
  end;
end;

//-----------------------------------------------------------------------------
//  ボタン付きエディットのCreateParams処理
//  WS_CLIPCHILDRENは描画するときに,子ウィンドウが占める領域を除外する指定
//  ウィンドウハンドルを持つコントロールが対象
//-----------------------------------------------------------------------------
procedure TplEditBitBtn.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  Params.Style := Params.Style or WS_CLIPCHILDREN;
end;

//-----------------------------------------------------------------------------
//  WM_SIZEメッセージ処理
//  ボタンの位置の設定とエディットコントロールの右マージンの設定
//-----------------------------------------------------------------------------
procedure TplEditBitBtn.WMSize(var Message: TWMSize);
var
  Flags : Integer;
begin
  inherited;
  if FButton <> nil then begin
    if NewStyleControls and Ctl3D then begin
      FButton.SetBounds(Width - FButton.Width - 4, 0, FButton.Width, Height - 4);
    end else begin
      FButton.SetBounds (Width - FButton.Width, 1, FButton.Width, Height - 2);
    end;
    Flags := EC_LEFTMARGIN or EC_RIGHTMARGIN;
    PostMessage(Handle, EM_SETMARGINS, Flags, MakeLParam(0, FButton.Width + 1));
  end;
end;

//-----------------------------------------------------------------------------
//  ボタンのOnClick処理
//-----------------------------------------------------------------------------
procedure TplEditBitBtn.ButtonClick (Sender: TObject);
begin
  if Assigned(FOnButtonClick) then begin
    FOnButtonClick(self);
  end;
  //以下は,実行時に生成してテストするためのもの
  //実際にこのコンポ(クラス)を利用する際は削除する
  MessageBox(Handle, 'ボタンが押された', 'テスト', MB_ICONINFORMATION);
end;

end.




02_エディットコントロール内にボタンを配置  - TSpeedButton


TSpeedButton はウィンドウハンドルを持っていないので,TBitBtn で使用したコードでは,再描画の現象を回避できません.
TSpeedButton の場合は,親であるエディットコントロールに,再描画する矩形領域を指定して,その領域以外は再描画しないようにします.この矩形領域は,エディットコントロールのサイズが変われば変化します.そこで,WM_SIZE メッセージを受信したら,再度設定し直します.
再描画の領域を設定するには,エディットコントロール自身に EM_SETRECTNP あるいは EM_SETRECTNP メッセージを送信します.
この方法は,ウィンドウハンドルを持つ TBitBtn, TButton でも使用可能です.

ただし,この方法は,エディットコントロールを複数行入力のコントロールにしないと有効になりません.単一行のだけの入力にしたい場合は,設定が必要です.MaskEdit の場合は,入力可能な位置と桁数が決まっています.その必要はありません.




図3
実行画面

リスト3
TSpeedButton を配置するエディットコントロールのクラス
unit plEditTSpeedButton;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Controls, Dialogs,
  StdCtrls, Buttons, Mask;

type
  TplEditSPButton = class(TCustomMaskEdit)
  private
    FButton: TSpeedButton;
    FOnButtonClick: TNotifyEvent;
    procedure SetEditRect;
    procedure WMSize(var Message: TWMSize); message WM_SIZE;
  protected
    procedure ButtonClick (Sender: TObject);
    procedure CreateParams(var Params: TCreateParams); override;
    procedure CreateWnd; override;
  public
    constructor Create(AOwner: TComponent); override;
  published
    property OnButtonClick: TNotifyEvent read FOnButtonClick write FOnButtonClick;
  end;

implementation

{ TplEditSPButton }

//-----------------------------------------------------------------------------
//  ボタン付きエディットのCreate処理
//-----------------------------------------------------------------------------
constructor TplEditSPButton.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  Width    := 160;
  EditMask := '!0000年 00月 00\日;1; ';
  EditText := FormatDateTime('yyyy年 MM月 dd日', Now);

  //ボタンを作成する
  FButton := TSpeedButton.Create (Self);
  with FButton do begin
    Parent  := Self;
    Width   := 18;
    CurSor  := crArrow;
    OnClick := ButtonClick;
    //Glyphの設定が必要であれば,ここでリソースから読込む
    //あるいは,Glyph設定用のプロパティを実装する
  end;
end;

//-----------------------------------------------------------------------------
//  ボタン付きエディットのCreateParams処理
//  StyleにES_MULTILINEが必要
//-----------------------------------------------------------------------------
procedure TplEditSPButton.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  Params.Style := Params.Style or ES_MULTILINE;
end;

//-----------------------------------------------------------------------------
//  ボタン付きエディットのCreateWnd処理
//  ハンドルが生成されてからSetEditRectを呼ぶ
// CreateWndはハンドルが生成されると自動的に実行されるメソッド
//-----------------------------------------------------------------------------
procedure TplEditSPButton.CreateWnd;
begin
  inherited CreateWnd;

  SetEditRect;
end;

//-----------------------------------------------------------------------------
//  エディットコントロールにEM_SETRECTNPメッセージを送信する処理
//  エディットコントロール編集領域以外を再描画の対象から除外する
//  これを有効にするには,エディットコントロールのStyleにES_MULTILINEが必要
//-----------------------------------------------------------------------------
procedure TplEditSPButton.SetEditRect;
var
  Loc : TRect;
begin
  SendMessage(Handle, EM_GETRECT, 0, LongInt(@Loc));
  Loc.Right := ClientWidth - FButton.Width - 2;
  SendMessage(Handle, EM_SETRECTNP, 0, LongInt(@Loc));
end;

//-----------------------------------------------------------------------------
//  WM_SIZEメッセージ処理
//  ボタンの位置の設定とエディットコントロールの右マージンの設定
//  TSpeedButtonの場合はSetEditRect内で設定で右マージンの設定になる 
//-----------------------------------------------------------------------------
procedure TplEditSPButton.WMSize(var Message: TWMSize);
var
  Flags : Integer;
begin
  inherited;
  if FButton <> nil then begin
    if NewStyleControls and Ctl3D then begin
      FButton.SetBounds(Width - FButton.Width - 4, 0, FButton.Width, Height - 4);
    end else begin
      FButton.SetBounds (Width - FButton.Width, 1, FButton.Width, Height - 2);
    end;
    SetEditRect;
  end;
end;

//-----------------------------------------------------------------------------
//  ボタンのOnClick処理
//-----------------------------------------------------------------------------
procedure TplEditSPButton.ButtonClick (Sender: TObject);
begin
  if Assigned(FOnButtonClick) then begin
    FOnButtonClick(self);
  end;
  //以下は,実行時に生成してテストするためのもの
  //実際にこのコンポ(クラス)を利用する際は削除する
  MessageBox(Handle, 'ボタンが押された', 'テスト', MB_ICONINFORMATION);
end;

end.


複数行 (マルチライン) の入力が可能なエディットコントロールでも,次のようにスタイルを設定すれば,事実上単一行の入力として使用できます.
ただし,[Ctrl] + [A] の操作による「全てを選択」が機能しなくなります.また,パスワード入力の際のマスク文字の機能も無効となります.これらの対策については,下記のコンポーネントのソースコードを参考にしてください.



//-----------------------------------------------------------------------------
//  xor ES_WANTRETURN  : Enterキーでは改行させない
//                       これだけだと[Alt] + [Enter]でスクロールしてしまう
//  xor ES_AUTOVSCROLL : 縦方向にスクロールさせない
//-----------------------------------------------------------------------------
procedure TplEditSPButton.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  Params.Style := Params.Style or ES_MULTILINE;
  Params.Style := Params.Style xor ES_WANTRETURN xor ES_AUTOVSCROLL;
end;




03_エディットコントロール内にボタンを配置  -  TButton


Delphi の TButton は,Windows の Button コントロールを使用したコントロールです.
Delphi 2009 になって,Windows Vista で採用された拡張スタイルに対応しています.Style プロパティがそれに相当しますが,ここでは,このプロパティを使用せず,メッセージ関係の処理でこの拡張スタイルを実装しています.



この項のサンプルは,Windows Vista 以降専用です.
Delphi 2009 以降で追加されたプロパティは使用していませんので,Delphi 2007 以前でも修正すれば使用可能です.例えば,Delphi 7 には TCustomButton はありません.代わりに TButton を使用します.また,定数類の定義はありません.Windows SDK やネットで調べることになります.
次は,実際の定数値の例です.


  TplArrowButton = class(TButton)

const
  BS_SPLITBUTTON = $0000000C;
  BCN_FIRST      = -1250;
  BCN_DROPDOWN   = BCN_FIRST + 2;


分割形式のスタイルにする例です.
TButto の Style プロパティを bsSplitButton にした場合に相当します.右側と左側のボタンのクリックは独立して検出できます.



図4
左側のボタンをクリックした場合
図5
右側の矢印のボタンをクリックした場合

リスト4
TButton を配置するエディットコントロールのクラス
unit plEditArrowButton;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Dialogs,
  StdCtrls, Buttons, CommCtrl;

type
  TplArrowButton = class(TCustomButton)
  protected
    procedure CreateParams(var Params: TCreateParams); override;
  end;

  TplEditArrowButton = class(TCustomEdit)
  private
    FButton: TplArrowButton;
    FOnButtonClick: TNotifyEvent;
    procedure WMSize(var Message: TWMSize); message WM_SIZE;
  protected
    procedure ButtonClick (Sender: TObject);
    procedure WMNotify(var Message: TWMNotify); message WM_NOTIFY;
    procedure CreateParams(var Params: TCreateParams); override;
  public
    constructor Create(AOwner: TComponent); override;
  published
    property OnButtonClick: TNotifyEvent read FOnButtonClick write FOnButtonClick;
  end;

implementation

{ TplArrowButton }

//-----------------------------------------------------------------------------
//  ボタンのCreateParams処理
//  ボタンのスタイルを設定する
//-----------------------------------------------------------------------------
procedure TplArrowButton.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  Params.Style := Params.Style or BS_SPLITBUTTON;
end;

{ TplEditArrowButton }

//-----------------------------------------------------------------------------
//  ボタン付きエディットのCreate処理
//-----------------------------------------------------------------------------
constructor TplEditArrowButton.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  Width    := 160;
  Height   := 50;
  AutoSize := False;

  //ボタンを作成する
  FButton := TplArrowButton.Create (Self);
  with FButton do begin
    Parent  := Self;
    Width   := 60;
    OnClick := ButtonClick;

  end;
end;

//-----------------------------------------------------------------------------
//  ボタン付きエディットのCreateParams処理
//  WS_CLIPCHILDRENは画するときに,子ウィンドウが占める領域を除外する指定
//  ウィンドウハンドルを持つコントロールが対象
//-----------------------------------------------------------------------------
procedure TplEditArrowButton.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  Params.Style := Params.Style or WS_CLIPCHILDREN;
end;

//-----------------------------------------------------------------------------
//  WM_SIZEメッセージ処理
//  ボタンの位置の設定とエディットコントロールの右マージンの設定
//-----------------------------------------------------------------------------
procedure TplEditArrowButton.WMSize(var Message: TWMSize);
var
  Flags : Integer;
begin
  inherited;
  if FButton <> nil then begin
    if NewStyleControls and Ctl3D then begin
      FButton.SetBounds(Width - FButton.Width - 4, 0, FButton.Width, Height - 4);
    end else begin
      FButton.SetBounds (Width - FButton.Width, 1, FButton.Width, Height - 2);
    end;
    Flags := EC_LEFTMARGIN or EC_RIGHTMARGIN;
    PostMessage(Handle, EM_SETMARGINS, Flags, MakeLParam(0, FButton.Width + 1));
  end;
end;

//-----------------------------------------------------------------------------
//  通知メッセージ処理
//  配置したボタンの矢印部分をクリックした時の通知メッセージを受信する
//  WM_NOTIFY のWM_COMMANDより情報が多く,デコードして取出すのは面倒である
//-----------------------------------------------------------------------------
procedure TplEditArrowButton.WMNotify(var Message: TWMNotify);
var
  pNMM : PNMMouse;
  Code : Integer;
begin
  inherited;

  pNMM := PNMMouse(Message.NMHdr);
  Code := pNMM^.hdr.code;
  if Code = BCN_DROPDOWN then begin
    //実行時に生成してテストするためのもの
    //実際にこのコンポ(クラス)を利用する際は削除する
    MessageBox(Handle, '矢印が押された', 'テスト', MB_ICONINFORMATION);
  end;
end;

//-----------------------------------------------------------------------------
//  ボタンのOnClick処理
//-----------------------------------------------------------------------------
procedure TplEditArrowButton.ButtonClick (Sender: TObject);
begin
  if Assigned(FOnButtonClick) then begin
    FOnButtonClick(self);
  end;
  //以下は,実行時に生成してテストするためのもの
  //実際にこのコンポ(クラス)を利用する際は削除する
  MessageBox(Handle, 'ボタンが押された', 'テスト', MB_ICONINFORMATION);
end;

end.


Button_SetSplitInfo 関数を使用すると,ボタンのスタイルを制御できます.
設定内容は TButtonSplitinfo レコード型 (Windows では BUTTON_SPLITINFO 構造体) の各メンバに値をセットします.Delphi 2007 以前にはこの関数はありません.MSDN の記事に書いてあるように,メッセージを使用します.BCM_GETSPLITINFO と BCM_SETSPLITINFO というメッセージです.

本サンプルでは,右ボタンとボタン全体の幅を同じにしていますが,この方法で,右ボタンの幅を任意の幅に変更できます.




図6
右のボタンの幅をボタンの幅と同じにした場合
図7
スプリットなしにした場合
  • 右のボタンの幅とボタン全体の幅を同じにして,スプリットなしにすると,右ボタンのクリックが検出される

リスト5
TButton を配置するエディットコントロールのクラス
分割ボタンのスタイルを変更する例
//-----------------------------------------------------------------------------
//  ボタン付きエディットのCreateWnd処理
//  ハンドルが生成されてからボタンのスタイルを取得・設定
// CreateWndはハンドルが生成されると自動的に実行されるメソッド
//-----------------------------------------------------------------------------
procedure TplEditArrowButton.CreateWnd;
var
  BtnInfo : TButtonSplitinfo;
begin
  inherited CreateWnd;

  //作成したボタンのTButtonSplitinfo構造体の値を取得
  Button_GetSplitInfo(FButton.Handle, BtnInfo);
//
  //ボタンのスタイルを設定
  BtnInfo.mask := BCSIF_SIZE or BCSIF_STYLE;
  //次の行を無効にすると,矢印(ボタンの右側)のクリックだけを検出する
  BtnInfo.uSplitStyle := BCSS_NOSPLIT;

  //ボタンの右側の幅をボタンの幅と同じにする
  BtnInfo.size.cx := FButton.Width;
  BtnInfo.size.cy := FButton.Height;
  //スタイルを設定
  Button_SetSplitInfo(FButton.Handle, BtnInfo);
end;


右のボタンの画像を,デフォルトの矢印ではなく,別の画像するサンプルコードです.
本サンプルでは,ディスクからビットマップ画像を読み込んでいますが,実際にはリソースから読込みことになるでしょう.
この画像は Glyph (グリフ - 複数の画像を 1 つにした) 形式ではありません.Enabled が False になった時に別の表示が必要な場合は,TImageList に複数の画像を格納しておきます.TImageList に格納されている順番によって機能が決まっています.この順番は Glyph と同じです.




図8
矢印ではなく,別の画像にする例


リスト6
TButton を配置するエディットコントロールのクラス
矢印以外の画像を描画する例
//-----------------------------------------------------------------------------
//  ボタン付きエディットのCreateWnd処理
//  ハンドルが生成されてからボタンのスタイルを取得・設定
// CreateWndはハンドルが生成されると自動的に実行されるメソッド
//-----------------------------------------------------------------------------
procedure TplEditArrowButton.CreateWnd;
var
  BtnInfo : TButtonSplitinfo;
  ABmp    : TBitmap;
begin
  inherited CreateWnd;

  //作成したボタンのTButtonSplitinfo構造体の値を取得
  Button_GetSplitInfo(FButton.Handle, BtnInfo);

  ABmp := TBitmap.Create;
  try
    //ビットマップ画像を読込んでImageListに追加する
    try
      ABmp.LoadFromFile('ABC_32bit.bmp');
      BtnInfo.himlGlyph := ImageList_Create(ABmp.Width, ABmp.Height,
                                            ILC_COLOR32, 1, 1);
      ImageList_Add(BtnInfo.himlGlyph, ABmp.Handle, 0);

      //スタイルのメンバを設定
      BtnInfo.mask        := BCSIF_IMAGE or BCSIF_SIZE or BCSIF_STYLE;
      BtnInfo.uSplitStyle := BCSS_IMAGE;
      BtnInfo.size.cx     := ABmp.Width  + 8;
      BtnInfo.size.cy     := ABmp.Height + 8;
    except
    end;
    //スタイルを設定
    Button_SetSplitInfo(FButton.Handle, BtnInfo);
  finally
    FreeAndNil(ABmp);
  end;
end;


次のようなコードにすると,通常の TButton のスタイルになります.
これは,TButton の Style プロパティの値を bsPushButton にした時と同じです.これが TButton の標準のスタイルです.実際には,スタイルの設定をしなければこのスタイルになります.



図9
通常のスタイルにした場合


リスト7
TButton を配置するエディットコントロールのクラス
従来の TButton と同じスタイルにする場合.スタイルを設定しない場合と同じ
//-----------------------------------------------------------------------------
//  ボタン付きエディットのCreateWnd処理
//  ハンドルが生成されてからボタンのスタイルを取得・設定
// CreateWndはハンドルが生成されると自動的に実行されるメソッド
//-----------------------------------------------------------------------------
procedure TplEditArrowButton.CreateWnd;
var
  BtnInfo : TButtonSplitinfo;
begin
  inherited CreateWnd;

  //作成したボタンのTButtonSplitinfo構造体の値を取得
  Button_GetSplitInfo(FButton.Handle, BtnInfo);

  //スタイルのメンバを設定
  BtnInfo.mask        := BCSIF_IMAGE or BCSIF_SIZE or BCSIF_STYLE;
  BtnInfo.uSplitStyle := BCSS_NOSPLIT;
  //スタイルを設定
  Button_SetSplitInfo(FButton.Handle, BtnInfo);
end;


コマンドリンクスタイルと呼ばれるスタイルにするサンプルです.
これは,TButton の Style プロパティの値を bsCommandLink にした時と同じです.



図10
コマンドリンクスタイルの TButton


リスト8
TButton を配置するエディットコントロールのクラス
コマンド リンクスタイルのボタンにする場合
unit plEditArrowButton;


interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Dialogs,
  StdCtrls, Buttons, CommCtrl;

type
  TplArrowButton = class(TCustomButton)
  protected
    procedure CreateParams(var Params: TCreateParams); override; 
  end;

  TplEditArrowButton = class(TCustomEdit)
  private
    FButton: TplArrowButton;
    FOnButtonClick: TNotifyEvent;
    procedure WMSize(var Message: TWMSize); message WM_SIZE;
  protected
    procedure ButtonClick (Sender: TObject);
    procedure CreateParams(var Params: TCreateParams); override;
    procedure CreateWnd; override;
  public
    constructor Create(AOwner: TComponent); override;
  published
    property OnButtonClick: TNotifyEvent read FOnButtonClick write FOnButtonClick;
  end;

implementation

{ TplArrowButton }

//-----------------------------------------------------------------------------
//  ボタンのCreateParams処理
//  ボタンのスタイルを設定する
//-----------------------------------------------------------------------------
procedure TplArrowButton.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  Params.Style := Params.Style or BS_COMMANDLINK;
end;

{ TplEditArrowButton }

//-----------------------------------------------------------------------------
//  ボタン付きエディットのCreate処理
//-----------------------------------------------------------------------------
constructor TplEditArrowButton.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  Width    := 200;
  Height   := 60;
  AutoSize := False;

  //ボタンを作成する
  FButton := TplArrowButton.Create (Self);
  with FButton do begin
    Parent  := Self;
    Width   := 80;
    OnClick := ButtonClick;
  end;
end;

//-----------------------------------------------------------------------------
//  ボタン付きエディットのCreateParams処理
//  WS_CLIPCHILDRENは画するときに,子ウィンドウが占める領域を除外する指定
//  ウィンドウハンドルを持つコントロールが対象
//-----------------------------------------------------------------------------
procedure TplEditArrowButton.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  Params.Style := Params.Style or WS_CLIPCHILDREN;
end;

//-----------------------------------------------------------------------------
//  ボタン付きエディットのCreateWnd処理
//  ハンドルが生成されてからボタンのスタイルを取得・設定
// CreateWndはハンドルが生成されると自動的に実行されるメソッド
//-----------------------------------------------------------------------------
procedure TplEditArrowButton.CreateWnd;
var
  TheText : String;
begin
  inherited CreateWnd;

  //ボタンに表示するテキストを設定してBCM_SETNOTEメッセージをボタンに送る
  TheText := '本日の' + sLineBreak + 'テスト';
  SendMessage(FButton.Handle, BCM_SETNOTE, 0, LPARAM(PChar(TheText)));
end;
//-----------------------------------------------------------------------------
//  WM_SIZEメッセージ処理
//  ボタンの位置の設定とエディットコントロールの右マージンの設定
//-----------------------------------------------------------------------------
procedure TplEditArrowButton.WMSize(var Message: TWMSize);
var
  Flags : Integer;
begin
  inherited;
  if FButton <> nil then begin
    if NewStyleControls and Ctl3D then begin
      FButton.SetBounds(Width - FButton.Width - 4, 0, FButton.Width, Height - 4);
    end else begin
      FButton.SetBounds (Width - FButton.Width, 1, FButton.Width, Height - 2);
    end;
    Flags := EC_LEFTMARGIN or EC_RIGHTMARGIN;
    PostMessage(Handle, EM_SETMARGINS, Flags, MakeLParam(0, FButton.Width + 1));
  end;
end;

//-----------------------------------------------------------------------------
//  ボタンのOnClick処理
//-----------------------------------------------------------------------------
procedure TplEditArrowButton.ButtonClick (Sender: TObject);
begin
  if Assigned(FOnButtonClick) then begin
    FOnButtonClick(self);
  end;
  //以下は,実行時に生成してテストするためのもの
  //実際にこのコンポ(クラス)を利用する際は削除する
  MessageBox(Handle, 'ボタンが押された', 'テスト', MB_ICONINFORMATION);
end;

end.




04_サンプルのクラスをコンポーネントとして登録するには


以下の赤の部分のコードを追加して,コンポーネントとしてインストール (登録) します.
RegisterComponents の最初の引数は,登録するコンポーネントパレットのタブ名です.ここで指定した名前のタブがない場合は,自動的に作成されます.次の引数が登録するクラス名です.
筆者の場合,procedure Register の宣言は,下のように implementation の直前に記述していますが,Register プロシージャの実装部分は,ユニットの最後に記述しています.
下のように implementation の直後に記述している方もいます.ユニットのコードの中間に記述する方はいないようです.

クラス名は,サンプルによっては重複している場合があります.各自で適当に変更してください.
また,テスト用に AutoSize を False にしています.この関係の設定は削除します.その他にもテスト用の余分なコードがあります.それらも修正してください.
クラスの継承やコンポーネントの作成が苦手という方も,この程度のコードであればなんとかなるのではないかと思われます.

継承クラスを,実行時に生成して使用するか,コンポーネントとして登録して使用可能にするかは,下のコードの赤字の部分があるか,ないかの違いです.
コンポーネントとして登録して使用可能にするためには,本ページのサンプルのように,継承クラスのコードを別のユニットにして保存しておく必要があります.




procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('plXRAY', [TplEditSPButton]);
end;