Version 7.17
オーナードローでカッコヨク!
「今回はちょっと面白いことをしてみましょう」
『面白いこと?』
「っていうか、ホントはデバイスコンテキスト自体結構面白いものなんだけ
どね。画面に描画できるわけだから」
『アプリに好きなよーに絵が描けるわけだもんね』
「今回教える【オーナードロー】は、デバイスコンテキストが使いたくなる
ような仕組みのひとつ。ダイアログに IDC_B_DRAW ボタンがあるでしょ」
『うんある。【描く!】って書いたの』
「これを【描く!】じゃなく、ちょっと派手にすることができるんです」
『それがオーナードローってヤツなんだ』
「または【オーナー描画】って言うのだけど、ここではオーナードローで統
一するね」
『オーナー描画って、漢字が分からない文字だけひらがなにしてるみたいで
なんか変……』
「オーナードローの、オーナーは【持ち主】、ドローは【描く】」
『ビルのオーナーとか言うもんね』
「そのビルに当たるのが、ダイアログ」
『ボタンを持ってるのはダイアログ、ってことね。ってことは、そのダイア
ログがボタンを描く?』
「そゆこと。普通はボタンが自分自身を描くんだけど、そうじゃなくてボタ
ンの持ち主のダイアログがボタンを描く、それがオーナードロー」
『……なんか難しそうなんだけど……』
「でもかなり簡単。特にボタンはね。とりあえずやってみようか。まずはク
ラスウィザードを開いて。メニューの【表示】から【 ClassWizard 】を選
んで」
『ダイアログ出たよ』
「【メッセージマップ】のページを開いて、【クラス名】は CAnimeDlg 、
【オブジェクト ID 】も CAnimeDlg にして」
『ほいほいほい』
「メッセージの中から WM_DRAWITEM を選んで、右上の【関数の追加】を押
してから【コード編集】を押すと」
『 CAnimeDlg::OnBDraw() の下にメンバ関数ができた』
void CAnimeDlg::OnDrawItem
(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CDialog::OnDrawItem(nIDCtl, lpDrawItemStruct);
}
「まずはこのメンバ関数が呼ばれるかどうか確認しましょう」
void CAnimeDlg::OnDrawItem
(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
{
TRACE( "Hit!\n" );
CDialog::OnDrawItem(nIDCtl, lpDrawItemStruct);
}
『んじゃビルドして実行』
「……」
『…… Hit! って出ないよ?』
「うん出ないよ」
『なんですとー!?』
「オーナードローは持ち主が描くわけだけど、そのためには描かれるもの、
つまりボタンの方から〈描いていいよ〉って許可しないといけないんです」
『あー、勝手に描けないんだ』
「そこで、ボタンの方で許可するようにします。ダイアログを閉じてから、
ダイアログエディタを開いて IDC_B_DRAW のプロパティを開いて」
『右クリックからプロパティっと』
「プロパティの【スタイル】ページに」
『【オーナー描画】がある! これオンにすればいいんだよね』
「そうそう。それでビルドして実行してみて」
『ほい。 Hit! って出た! げ、でもボタンが消えた!』
「消えたんじゃなくて、【描く!】とか、立体的に見せる縁とかも描かれな
くなっただけ。 CAnimeDlg::OnBDraw() に TRACE() でも置いて」
『この辺にボタンあるかなーってとこで押すと、ちゃんと TRACE() のが出
てくるね。見えてないだけなんだ』
「というわけで、見えるようにしましょう。そのためのメンバ関数が
CAnimeDlg::OnDrawItem() 。このメンバ関数は、 WM_DRAWITEM ってメッ
セージが送られてきた時に呼ばれるメンバ関数……」
『……』
「……つまり、ボタンが押された時に CAnimeDlg::OnBDraw() が呼ばれるよ
うに、ボタンの描画が必要な時に呼ばれるメンバ関数」
『描画が必要な時っていうと、 CAnimeDlg::OnPaint() みたいな感じ?』
「ほとんど同じ。ただタイミングとか違う部分も多いけどね。そうそう、
CAnimeDlg::OnPaint() に前に追加したのは消しちゃっていいから」
『続き物って感じで教えないね』
「プログラムは小さく小さく、が重要だからね。と、それは置いといて、実
際にボタンを描画してみましょう。まずボタンの ID が第1引数の nIDCtl
に入ってるからそれを確認」
『ボタンの ID ってことは、 IDC_B_DRAW だね。でもこれって何のため?』
「いくつもボタンをオーナードローにしちゃったら」
『そっか、それぞれ別の描画しなきゃいけないから』
「そのためのチェックね。で、ボタンのデバイスコンテキストが第1引数の
lpDrawItemStruct の hDC メンバ変数に入ってます」
『 LPDRAWITEMSTRUCT って初見』
「頭の LP は?」
『ポインタの印だね。それ取ると、 DRAWITEMSTRUCT 』
「これが構造体になってて、オーナードローに必要な情報が全部入ってるか
ら、これを使って描画すればオーケー」
『 BeginPaint() とか GetDC() とか』
「の準備や後片付けは必要ないから。このメンバ関数が呼ばれる前に準備さ
れてるってことかな」
『便利ねー』
「それではやってみましょう」
void CAnimeDlg::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
{
// ここから追加。
if( nIDCtl == IDC_B_DRAW )
{
HBRUSH hBrush;
hBrush = ::CreateSolidBrush( RGB( 0, 0, 255 ) );
::FillRect
( lpDrawItemStruct->hDC
, &( lpDrawItemStruct->rcItem )
, hBrush
);
::DeleteObject( hBrush );
}
// 追加ここまで。
CDialog::OnDrawItem(nIDCtl, lpDrawItemStruct);
}
『 nIDCtl がそのボタンの ID か調べて、そうならボタンを塗り潰し?』
「 DRAWITEMSTRUCT::hDC にボタンのデバイスコンテキストが入ってて、
DRAWITEMSTRUCT::rcItem はボタンの四角が入ってる RECT 構造体だから」
『あとはいつもの塗りつぶしね。 FillRect() の第2引数が分かりにくいけ
ど……』
「前も言ったけど、これは lpDrawItemStruct-> の部分は無視するのがコ
ツ。だから &rcItem と同じ」
『 Ver 6.02 ( No.102 ) の話ね。んじゃビルドして実行。を! ボタンが
真っ青!』
「こういうふうに青く塗りつぶせるわけです。これは無味簡素だけど」
『がんばれば好きなようなボタンが描けるわけねー。なんかすごい』
「ま、実際にはここからの方が大変かもね」
『でもさ、ボタンの枠まで消えちゃうってちょっとビックリかも。だって、
これじゃボタン押したのわからないじゃない』
「そうじゃなくて、ボタン押した時にはボタン押した時の描画をしなきゃい
けないわけ。ボタンはそうだったわけだから」
『ボタンを押したらへこむのは、ボタンがへこんだように見えるように描い
てた!』
「そういうこと。だからオーナードローのときも」
『へこんだらへこんだように見せなきゃいけない! って、そんなことでき
るの?』
「もちろん。 DRAWITEMSTRUCT のリファレンスを読むと」
『色々パラメーターがあるんだね』
「押されたかどうかは itemState メンバ変数を調べればわかります。この
中の ODS_SELECTED ってビットフラグが立ってたら〈押されてる〉状態。
ビットフラグ調べる方法って憶えてる?」
『う”』
「 == の代わりに & で比較すればいいから。 Ver 4.05 ( No.055 ) を参考
にね」
『普段使わないと忘れちゃうね……』
「というわけで使ってみましょう」
void CAnimeDlg::OnDrawItem
(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
{
// ここから追加。
if( nIDCtl == IDC_B_DRAW )
{
HBRUSH hBrush;
if( lpDrawItemStruct->itemState & ODS_SELECTED )
{
hBrush = ::CreateSolidBrush( RGB( 255, 0, 0 ) );
}
else
{
hBrush = ::CreateSolidBrush( RGB( 0, 0, 255 ) );
}
::FillRect
( lpDrawItemStruct->hDC
, &( lpDrawItemStruct->rcItem )
, hBrush
);
::DeleteObject( hBrush );
}
// 追加ここまで。
CDialog::OnDrawItem(nIDCtl, lpDrawItemStruct);
}
『んー長いけど、さっきの DRAWITEMSTRUCT::itemState に ODS_SELECTED
が入ってるか調べてそれで……ブラシの色を変えてる!』
「 ODS_SELECTED が含まれてる、つまりボタンが押されてる時は赤色、それ
以外は青色」
『んじゃ実行。おお!! ボタン押すと赤くなる!』
「こうなるとボタンらしくなるでしょ」
『なるなる! っつーかこれなんかカッコイイ!』
「だねー。で、最後に重要なこと」
『……またミスとか?』
「違います。ここで重要なのは、こういうオーナードロー的なことを、普段
のボタンもしてるってこと」
『うん、それはさっき聞いた』
「そして、ボタンもウィンドウの一種。つまり、ウィンドウを作って、
CAnimeDlg::OnPaint() や CAnimeDlg::OnDrawItem() が呼ばれたときにボタ
ンらしく描画すれば」
『ボタンになる? ってことは、あたしがボタン作れる!?』
「将来的には作れるようになってもらうし、ウィンドウズのボタンだって所
詮は」
『げ、出た。つまりフツーのプログラマーが作ったもんだって言いたいんで
しょ?』
「そゆこと。こういう、元々存在するシステムのものっていうのはすごいも
のに見えるけど、でもそうじゃないんだってことは忘れないでね」
『はいはいはい、耳にたこができますよーだ』
/*
Preview Next Story!
*/
『何かすると呼ばれるって多いよね』
「うん、そうしないと CPU が休めないから」
『げ、これって CPU が楽をするためのシステム!? ダメくさい!』
「それがね、 CPU が暇じゃないと全部のアプリを見て回れないんだよね」
『なになに、じゃぁ暇っていうのはいいことなの?』
「必要不可欠。というわけで次回」
< Version 7.18 タイマーで止めない! >
『につづく!』
「暇は重要だよ」
『えー、暇ってなんかヤダ!』
「若いねー」
『水希ちゃんじじくさ!』