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 |