| history | TOP |
2007/11/27:作成
| overview | TOP |
タイトルにあるとおりです。自分で作ったプログラムのキャプションバー(タイトルバー)の部分に、ボタンをつけてクリックイベントを拾ってみました。
もちろん自分ではほとんどやらずに、参考サイトからパクってきたソースコードをチョロっとイジっただけです。って実際にはかなり時間かけてしまってますが...
やっぱり飽きないためには、すでに動くものを少しずつ修正していくのが一番です。こういう好奇心優先のものの場合は、特にそうです。いきなり C++ のソースから翻訳し始めると、終盤にならないと動かないので翻訳中に飽きてしまう可能性がかなり高いです。なので、今回は Delphi ですでに完成されているのを下敷きにしました。もうその時点で好奇心もへったくれもないわけですが、そこはそれ。
| 参考サイト | TOP |
いつも興味深いサンプルが見つかる Code Project のは、C++ でかなりがっちり書かれていて、Delphi で書き直す気になれませんでした。
もうひとつの方(サイトの名前失念です)は、もろの回答です。で、こっちです。ただ、そのままはさすがにアレなので、2個以上のボタンを作れるようにしました。
| ダウンロード | TOP |
20071127CaptionButton.zip(199,370Bytes)
テストに使ったソースコードと実行ファイルです。
| スクリーンショット | TOP |
起動時です。次回自分で見たときの覚書を思いっきり書いてあります。
それはそうとキャプションバーに必要以上にボタンが並んでいるのが見えます。
右側の3つは標準のボタンですが、それ以外は全部今回のテストで表示しています。
何個目かの作ったボタンを押しているところです。押し心地はイマイチです。
とりあえずバージョン情報を表示してみました。ちゃんとクリックを取れているように見えます。見た感じいい感じです。
| 使い方 | TOP |
上のサンプルは、後述するクラスを使って結構あっさりと実現できています。といってみる。
クラスを作って、キャプションを表示したいフォームを引数に渡します。描画したいフォントを伝えて、クリックイベントを接続して、後はボタンを追加(AddButton())して、ついでにキャプションを設定するだけです。邪魔くさいか。
そうすると、クリックされたときに、作った順番に対応した Index 付き(ゼロベース)でクリックイベントが発生するので、いろいろやってみると少し楽しいです。
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, CaptionButton, StdCtrls;
type
TForm1 = class(TForm)
(略)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
FCapBtns: TCaptionButtons;
procedure OnCapBtnClick(const Index: Integer);
public
end;
var
Form1: TForm1;
implementation
uses
Info;
{$R *.dfm}
//-----------------------------------------------------------------------------
// 起動時
procedure TForm1.FormCreate(Sender: TObject);
begin
// ボタン管理クラス作成
FCapBtns := TCaptionButtons.Create(Self);
FCapBtns.Font.Assign(LblFont.Font);
FCapBtns.OnClick := OnCapBtnClick;
// ボタン追加
FCapBtns.AddButton().Caption := '1';
FCapBtns.AddButton().Caption := '2';
FCapBtns.AddButton().Caption := '3';
FCapBtns.AddButton().Caption := '4';
FCapBtns.AddButton().Caption := '5';
FCapBtns.AddButton().Caption := '6';
FCapBtns.AddButton().Caption := '7';
FCapBtns.AddButton().Caption := '8';
FCapBtns.AddButton().Caption := '9';
end;
//-----------------------------------------------------------------------------
// 終了時
procedure TForm1.FormDestroy(Sender: TObject);
begin
FCapBtns.Free();
end;
//-----------------------------------------------------------------------------
// キャプションボタンクリック時
procedure TForm1.OnCapBtnClick(const Index: Integer);
begin
// 作った順番と処理の対応は自分でやる。残念。
case Index of
0: OpenFilePropertyDialog(ParamStr(0));
1: OpenFilePropertyDialog(ExtractFilePath(ParamStr(0)));
2: AppMessageBox(GetOsInfo(), MB_OK or MB_ICONINFORMATION);
else
OpenInfoForm();
end;
end;
end.
| ソースコード | TOP |
そんなに書くこともないので、キャプションバーにボタンを置いてクリックを判定するクラスのソースコードを掲載します。
他人の成果を勝手に公開しているようなものなので、言い訳代わりに参考にしたプログラムと違う点を挙げてみます。
//-----------------------------------------------------------------------------
// タイトルバーにボタンを配置してクリックに反応するためのクラス
// BlueLeaf1336 http://www.geocities.jp/fjtkt/
//-----------------------------------------------------------------------------
// 2007.11.26 DrawNewButton を元にクラスにまとめた
// 複数のボタンに対応
// 2007.11.27 TO DO ■Caption or Glyph
// 2007.11.27 TO DO ■OnClick()
// TO DO □トグル形式のボタン
//-----------------------------------------------------------------------------
// 参考プログラム
//1----------------------------------------------------------------------------
// class CCaptionButton
// Add push buttons to the caption bar
//
// Author: Yao Zhifeng
// Contact: yaozhifeng@hotmail.com
//2----------------------------------------------------------------------------
// ダウンロードサイト失念
// NewButton.lzh
// unit DrawNewButton
unit CaptionButton;
interface
uses
Windows, Messages, Classes, SysUtils, Contnrs, Forms, Graphics;
type
TOnCaptionButtonClick = procedure (const Index: Integer) of object;
TCaptionButtons = class;
TCaptionButton = class
protected
Pushed: Boolean;
Parent: TCaptionButtons;
public
Caption: String;
procedure Draw(const AHDC: HDC; const ARect: TRect);
constructor Create();
destructor Destroy(); override;
end;
TCaptionButtons = class
protected
FTarget :TForm;
FOriginalWndProc: Pointer;
FHookWndProc :Pointer;
FWndHandle :HWND;
procedure WndProc(var Msg: TMessage); virtual;
protected
FButtons: TObjectList;
FFont: TFont;
FOnClick: TOnCaptionButtonClick;
function CaptionHeight(): Integer;
function CaptionBarRect(): TRect;
function DefaultButtonCount(): Integer;
function LeftEndDefaultButtonRect(): TRect;
protected
procedure DrawButtons();
function LeftButtonDown(const APoint: TPoint): Boolean;
function LeftButtonUp(const APoint: TPoint): Boolean;
public
property Font: TFont read FFont;
property OnClick: TOnCaptionButtonClick read FOnClick write FOnClick;
public
constructor Create(ATarget: TForm);
destructor Destroy(); override;
function AddButton(): TCaptionButton;
end;
implementation
//-----------------------------------------------------------------------------
//■ TCaptionButton
//-----------------------------------------------------------------------------
// コンストラクタ
constructor TCaptionButton.Create();
begin
inherited Create();
Pushed := False;
end;
//-----------------------------------------------------------------------------
// デストラクタ
destructor TCaptionButton.Destroy();
begin
inherited Destroy();
end;
//-----------------------------------------------------------------------------
// 描画
procedure TCaptionButton.Draw(const AHDC: HDC; const ARect: TRect);
var
LRect: TRect;
LState: Cardinal;
LFormat: Cardinal;
LParams: TDrawTextParams;
LOldMode: Integer;
begin
// ボタンを描画する
LState := DFCS_BUTTONPUSH;
if (Pushed) then LState := LState or DFCS_PUSHED;
DrawFrameControl(AHDC, ARect, DFC_BUTTON, LState);
// キャプションを描画するための設定
LRect := ARect;
OffsetRect(LRect, -1, -1); // 実験値。しかもイマイチ。
LFormat := DT_CENTER or DT_VCENTER or DT_SINGLELINE;
FillChar(LParams, SizeOf(LParams), 0);
LParams.cbSize := SizeOf(LParams);
// 押されているときは少しずらす
if (Pushed) then OffsetRect(LRect, 1, 1);
// 背景透過
LOldMode := SetBkMode(AHDC, TRANSPARENT);
DrawTextEx(AHDC, PChar(Caption), -1, LRect, LFormat, @LParams);
SetBkMode(AHDC, LOldMode);
end;
//-----------------------------------------------------------------------------
//■ TCaptionButtons
//-----------------------------------------------------------------------------
// コンストラクタ
constructor TCaptionButtons.Create(ATarget: TForm);
begin
inherited Create();
// ボタン保持用リスト作成
FButtons := TObjectList.Create(True);
// フォント作成
FFont := TFont.Create();
// クリックイベント
FOnClick := nil;
// のっとりコントロール保存
FTarget := ATarget;
// そのハンドル保存
FWndHandle := FTarget.Handle;
// 置き換えWindowProcedureをオブジェクトに
FHookWndProc := Classes.MakeObjectInstance(WndProc);
// 置き換えてオリジナルを保存
FOriginalWndProc := Pointer(SetWindowLong(FWndHandle, GWL_WNDPROC,
LongInt(FHookWndProc)));
end;
//-----------------------------------------------------------------------------
// デストラクタ
destructor TCaptionButtons.Destroy();
begin
// 元に戻そう
SetWindowLong(FWndHandle, GWL_WNDPROC, LongInt(FOriginalWndProc));
// オブジェクトも破棄
Classes.FreeObjectInstance(FHookWndProc);
// フォント破棄
FFont.Free();
// ボタン保持用リスト破棄
FButtons.Free();
inherited Destroy();
end;
//-----------------------------------------------------------------------------
// ボタン描画追加
function TCaptionButtons.AddButton(): TCaptionButton;
begin
Result := TCaptionButton.Create();
Result.Parent := Self;
FButtons.Add(Result);
end;
//-----------------------------------------------------------------------------
// ボタン描画
procedure TCaptionButtons.DrawButtons();
var
i: Integer;
LRect: TRect;
LBtnWidth: Integer;
LButton: TCaptionButton;
LHDC: HDC;
LOldObj: HGDIOBJ;
begin
// 標準ボタン左端
LRect := LeftEndDefaultButtonRect();
// ボタンの幅
LBtnWidth := CaptionHeight();// GetSystemMetrics(SM_CXSIZE);
// 描画先確保
LHDC := GetWindowDC(FWndHandle);
LOldObj := SelectObject(LHDC, FFont.Handle);
// 少しずつずらして描画
for i := 0 to FButtons.Count - 1 do
begin
LButton := TCaptionButton(FButtons.Items[i]);
OffsetRect(LRect, -LBtnWidth, 0);
LButton.Draw(LHDC, LRect);
end;
// 描画先解放
SelectObject(LHDC, LOldObj);
ReleaseDC(FWndHandle, LHDC);
end;
//-----------------------------------------------------------------------------
// 作ったボタンが押されかかってるかチェック
function TCaptionButtons.LeftButtonDown(const APoint: TPoint): Boolean;
var
i: Integer;
LRect: TRect;
LBtnWidth: Integer;
LButton: TCaptionButton;
LWndRect: TRect;
begin
Result := False;
// 標準ボタン左端
LRect := LeftEndDefaultButtonRect();
// ウィンドウの左上に原点をあわせる
GetWindowRect(FWndHandle, LWndRect);
OffsetRect(LRect, LWndRect.Left, LWndRect.Top);
// ボタンの幅
LBtnWidth := CaptionHeight();// GetSystemMetrics(SM_CXSIZE);
// 少しずつずらしてチェック
for i := 0 to FButtons.Count - 1 do
begin
LButton := TCaptionButton(FButtons.Items[i]);
OffsetRect(LRect, -LBtnWidth, 0);
LButton.Pushed := PtInRect(LRect, APoint);
if (LButton.Pushed) then Result := True;
end;
end;
//-----------------------------------------------------------------------------
// ボタンが押されたかどうかチェック
function TCaptionButtons.LeftButtonUp(const APoint: TPoint): Boolean;
var
i: Integer;
LRect: TRect;
LBtnWidth: Integer;
LButton: TCaptionButton;
LWndRect: TRect;
LClickIndex: Integer;
begin
Result := False;
LClickIndex := -1;
// 標準ボタン左端
LRect := LeftEndDefaultButtonRect();
// ウィンドウの左上に原点をあわせる
GetWindowRect(FWndHandle, LWndRect);
OffsetRect(LRect, LWndRect.Left, LWndRect.Top);
// WM_LBUTTONUP でクリックを検出しているので座標調整
// ※クライアント領域左上が原点になる
OffsetRect(LRect, - FTarget.ClientOrigin.X, - FTarget.ClientOrigin.Y);
// ボタンの幅
LBtnWidth := CaptionHeight();// GetSystemMetrics(SM_CXSIZE);
// 少しずつずらしてチェック
for i := 0 to FButtons.Count - 1 do
begin
LButton := TCaptionButton(FButtons.Items[i]);
OffsetRect(LRect, -LBtnWidth, 0);
if (PtInRect(LRect, APoint) and LButton.Pushed) then
begin
LClickIndex := i;
Result := True;
end;
LButton.Pushed := False;
end;
// クリックイベント生成
if ((LClickIndex > -1) and Assigned(FOnClick)) then
begin
FOnClick(LClickIndex);
end;
end;
//-----------------------------------------------------------------------------
// 差し込むウィンドウプロシージャ
procedure TCaptionButtons.WndProc(var Msg: TMessage);
var
LHandled: Boolean;
LWMNCLBUTTONDOWN: TWMNCLBUTTONDOWN;
LWMLBUTTONUP: TWMLBUTTONUP;
begin
LHandled := False;
case Msg.Msg of
WM_CAPTURECHANGED,
WM_ENTERIDLE,
WM_NCPAINT,
WM_SETICON,
WM_PAINT,
WM_SYSCOMMAND,
WM_NCACTIVATE,
WM_SIZE,
WM_ACTIVATE,
WM_SETCURSOR:
begin
DrawButtons();
end;
WM_NCLBUTTONDOWN:
begin
LWMNCLBUTTONDOWN := TWMNCLBUTTONDOWN(Msg);
LHandled := LeftButtonDown(Point(LWMNCLBUTTONDOWN.XCursor, LWMNCLBUTTONDOWN.YCursor));
if (LHandled) then SetCapture(FWndHandle);
DrawButtons();
end;
WM_LBUTTONUP:
begin
LWMLBUTTONUP := TWMLBUTTONUP(Msg);
LHandled := LeftButtonUp(Point(LWMLBUTTONUP.XPos, LWMLBUTTONUP.YPos));
ReleaseCapture();
DrawButtons();
end;
// WM_NCHITTEST : OnNcPaint(Msg, LHandled);
// WM_NCLBUTTONDOWN: OnNcLButtonDown(Msg, LHandled);
// WM_LBUTTONUP : OnLButtonUp(Msg, LHandled);
// WM_MOUSEMOVE : OnMouseMove(Msg, LHandled);
// WM_ACTIVATE : OnActivate(Msg, LHandled);
end;
// それはそうとオリジナルも実行する
if (not LHandled) then
begin
Msg.Result := CallWindowProc(FOriginalWndProc, FWndHandle,
Msg.Msg, Msg.wParam, Msg.lParam);
end;
end;
//------------------------------------------------------------------------------
// キャプションの高さ
function TCaptionButtons.CaptionHeight(): Integer;
begin
// ツールバー型
if (FTarget.BorderStyle in [bsToolWindow, bsSizeToolWin]) then
begin
Result := GetSystemMetrics(SM_CYSMCAPTION);
end
// 標準のウインドウ
else
begin
Result := GetSystemMetrics(SM_CYCAPTION);
end;
end;
//------------------------------------------------------------------------------
// タイトルバーのサイズを取得
function TCaptionButtons.CaptionBarRect(): TRect;
var
LCxSize: Integer;
LCySize: Integer;
begin
// 一重線のウインドウの枠の太さ
if (FTarget.BorderStyle in [bsDialog, bsSingle, bsToolWindow]) Then
begin
LCxSize := GetSystemMetrics(SM_CXFIXEDFRAME);
LCySize := GetSystemMetrics(SM_CYFIXEDFRAME);
end
else
// 二重線のウインドウの枠の太さ
begin
LCxSize := GetSystemMetrics(SM_CXSIZEFRAME);
LCySize := GetSystemMetrics(SM_CYSIZEFRAME);
end;
// ウインドウサイズ取得
GetWindowRect(FWndHandle, Result);
// 左上を原点に移動
OffsetRect(Result, - Result.Left, - Result.Top);
// 枠の分を削る
InflateRect(Result, - LCxSize, - LCySize);
// 高さ調整
Result.Bottom := Result.Top + CaptionHeight() - 1;
end;
//-----------------------------------------------------------------------------
// タイトルバーにある標準ボタンの数を数える
function TCaptionButtons.DefaultButtonCount(): Integer;
var
LButtons: TBorderIcons;
begin
Result := 0;
LButtons := FTarget.BorderIcons;
case FTarget.BorderStyle of
bsDialog:
begin
if (biSystemMenu in LButtons) then
begin
// 閉じる
Inc(Result);
// ヘルプ
if (biHelp in LButtons) then Inc(Result);
end;
end;
bsNone:
begin
// なし
end;
bsSingle, bsSizeable:
begin
if (biSystemMenu in LButtons) then
begin
// 閉じる
Inc(Result);
// 閉じる以外にボタンがひとつ
if (LButtons = [biSystemMenu, biMinimize])
or (LButtons = [biSystemMenu, biMaximize])
or (LButtons = [biSystemMenu, biHelp]) then
Inc(Result, 1)
// 閉じる以外にボタンがふたつ
else
if (LButtons = [biSystemMenu, biMinimize, biMaximize, biHelp])
or (LButtons = [biSystemMenu, biMinimize, biMaximize]) then
Inc(Result, 2)
// 閉じる以外にボタンがみっつ
else
if (LButtons = [biSystemMenu, biMinimize, biHelp])
or (LButtons = [biSystemMenu, biMaximize, biHelp]) then
Inc(Result, 3)
;
end;
end;
bsSizeToolWin, bsToolWindow:
begin
// 閉じる
if (biSystemMenu in LButtons) then Inc(Result);
end;
end;
end;
//------------------------------------------------------------------------------
// 左端にある標準ボタンの領域を取得
function TCaptionButtons.LeftEndDefaultButtonRect(): TRect;
var
LCxSize: Integer;
LBtnNum: Integer;
begin
// 通常ボタンの横を取得
LCxSize := GetSystemMetrics(SM_CXSIZE);
// ボタンの数
LBtnNum := DefaultButtonCount();
// 標準のボタンのある領域を決定
Result := CaptionBarRect();
InflateRect(Result, 0, - 2); // 実験値
Result.Left := Result.Right - LBtnNum * LCxSize;
// 一番左端のボタンの領域を決定
Result.Right := Result.Left + LCxSize - 2;
// ツールバー型のスタイルだったら
if (fTarget.BorderStyle in [bsToolWindow, bsSizeToolWin]) then
begin
InflateRect(Result, -3, 0); // 実験値
OffsetRect(Result, 6, 0); // 実験値
end;
end;
end.
| イメージトレーニング | TOP |
まったく当てもなく無責任に妄想してみます。
アレか。タスクバーでええやん。アドレスバーとか。
| EOF | TOP |