Version 7.16
再描画させる!
「前回は再描画の方法を紹介しました」
『再描画する時には CAnimeDlg::OnPaint() が呼ばれるから、その中でデバ
イスコンテキスト使えばいいって話だったよね』
「そのときのルールは?」
『 BeginPaint() と EndPaint() を使うってことだったよね』
「そうそう。それさえ守ればこれまでと同じ。でも」
『でも?』
「これまでの〈ボタンを押した時に描画〉してたわけでしょ。つまりいつで
もできるっていう」
『うん。そっか、再描画だと再描画する時に描くから、好きなタイミングで
描くってできないんだ』
「でも、ボタン押した時だと」
『再描画したら消えちゃう』
「というわけで方針は、まず最初は再描画時に何も描かないようにします」
『まっさらにするわけね』
「次に、ボタン押したら〈再描画時に描く〉ように設定を変えます」
『どうやって?』
「メンバ変数を使うことになるかな」
『なんだか面倒なことになりそう……』
「で、これだけだと再描画しないと反映されない」
『わざとウィンドウ後ろに隠す?』
「そんなことしなくても〈再描画するように指示する〉ことができるから」
『そういう API があるわけね』
「そういうこと。今回はその辺を紹介しましょう。まずは、
Ver 7.14 ( No.134 ) のままなら、 AnimeDlg.h の部分が」
class CAnimeDlg : public CDialog
{
CRgn m_cRgn;
CFont m_cFont;
// 以下略。
『うん、このまま』
「このふたつのメンバ変数を取り除いて、ひとつメンバ変数を追加してくだ
さい」
class CAnimeDlg : public CDialog
{
// 以下1行追加。
BOOL m_bDrawOk;
// 以下略。
『これ何に使うの?』
「これは〈再描画するかしないか〉のフラグに使います。これが FALSE な
ら〈描画しない〉、 TRUE なら〈描画する〉ってことにします」
『ふむふむ』
「で、このままだと m_bDrawOk の中には何も入ってません。最初は〈描画
しない〉ってことにしたいから、最初は FALSE を入れておきます。ダイア
ログが開く時に呼ばれるメンバ関数は?」
『 CAnimeDlg::OnInitDialog() ! いつもどーり // TODO: のあと?』
「そう。これも Ver 7.14 ( No.134 ) のが残ってたら削除していいから」
BOOL CAnimeDlg::OnInitDialog()
{
// 略。
// TODO: 特別な初期化を行う時はこの場所に追加してください。
// 以下から追加。
m_bDrawOk = FALSE;
// 追加ここまで。
return TRUE; // TRUE を返すとコントロールに設定した(略)。
}
『ダイアログが開くのは最初の一度だけ、だからここで FALSE を入れちゃ
えばいいわけね』
「そゆこと。で、最後に描画部分。前回最後のとこをこう変えます」
void CAnimeDlg::OnPaint()
{
if (IsIconic())
{
// 略。
}
else
{
PAINTSTRUCT stPaintStruct;
HDC hDC
= ::BeginPaint
( m_cCanvasStatic.GetSafeHwnd()
, &stPaintStruct
);
// m_bDrawOk が FALSE じゃない時だけ描画します。
if( m_bDrawOk != FALSE )
{
const char pchText[] = "あいうえお";
::TextOut( hDC, 0, 0, pchText, strlen( pchText ) );
}
::EndPaint
( m_cCanvasStatic.GetSafeHwnd()
, &stPaintStruct
);
CDialog::OnPaint();
}
}
『前回のと比べると、 TextOut() 呼ぶとこが if に囲まれて、それがさっ
きのフラグで決められてる、ってとこかな』
「とりあえずこれで実行してみて」
『え? だって、これじゃフラグが TRUE にならないから、絶対に描かれな
いけど』
「うん、その確認」
『あ、そっか。ビルドして実行。うん、何も描かれないね』
「ならオーケー。それじゃ、今度は〈ボタンを押したら描画する〉ようにし
ます。いつものボタンのメンバ関数を次のようにしてください」
void CAnimeDlg::OnBDraw()
{
m_bDrawOk = TRUE;
::InvalidateRect( GetSafeHwnd(), NULL, FALSE );
}
『まずフラグを TRUE にして、で、初めて見る API だね』
「 InvalidateRect() は〈更新する領域〉を変える API 」
『???』
「うん、それはあとでね。とりあえずここでは〈再描画を命令する〉って機
能だけ憶えればいいかも」
『そっか、これを呼べばウィンドウを隠したりしなくても再描画される、つ
まり CAnimeDlg::OnPaint() が呼ばれるんだ』
「そゆこと」
『んじゃビルドして実行。最初は何も出ないけど、ボタンを押すと……あい
うえおって出た!』
「で、一度隠して」
『表に出させて、再描画させてもちゃんと出る!』
「こうすれば、好きなタイミングで描画できて、しかも再描画できるわけ」
『なるほど。あ!』
「な、なに?」
『この前さ、〈描画せずに再描画だけする〉ってのが本当だって言ってたの
はこれなんだ!』
「前回のだね。そうそう、こういうふうに再描画で描画するのが基本。そう
しないと再描画したら消えちゃうからね」
『ほー。あと質問』
「はい火美ちゃん」
『前回からなんだけど、 BeginPaint() と EndPaint() 使って描くと、背景
の色が消えちゃうんだけど』
「ぎく! それ、なんでだろうね……」
『水希ちゃんも知らないわけぇ?』
「うん……。でも、こっちのダイアログの色と同じ、っていう方がたぶん正
しい色だと思うから、今回みたいに BeginPaint() と EndPaint() をいつも
呼ぶようにしといて」
『この前の事もあるし、なーんか最近頼りないってゆーか』
「う”……は、話を変えよう!」
『また強引に』
「さっきの InvalidateRect() の話。 CAnimeDlg::OnPaint() の中に次の
TRACE() を加えて」
void CAnimeDlg::OnPaint()
{
if (IsIconic())
{
// 略。
}
else
{
PAINTSTRUCT stPaintStruct;
HDC hDC
= ::BeginPaint
( m_cCanvasStatic.GetSafeHwnd()
, &stPaintStruct
);
// 以下を追加。
TRACE
( "%d, %d, %d, %d\n"
, stPaintStruct.rcPaint.left
, stPaintStruct.rcPaint.top
, stPaintStruct.rcPaint.right
, stPaintStruct.rcPaint.bottom
);
// 略
}
}
『 stPaintStruct のメンバ変数を表示してるんだね』
「これでビルドしてから、ダイアログの上を他のウィンドウで少しだけ隠し
たりしてみると」
『あ、値が変わる!』
「このメンバ変数は RECT 型なんだけど、この中に更新領域、つまり〈どの
領域が新しく見えるようになったか〉が入ってるんです」
『つまり、他のウィンドウで隠してどけるってした時に出た部分が入ってる
んだ。でもそれ、何に使うの?』
「グラフィックアプリだと、再描画に時間がかかる場合があるから」
『そっか、これ使えば毎回全部再描画しなくて済むんだ!』
「そのための情報がここに入ってるわけ。で、さっきの InvalidateRect()
はその指定もできるんです。あ、一応今のままでボタン押してみて」
『ほい。更新領域は何度押しても変わらないね』
「うん、変わらないのは当たり前。ダイアログのクライアント部分全体だか
らね」
『全体が再描画するからそうなるんだ』
「で、それを変えてみます」
void CAnimeDlg::OnBDraw()
{
m_bDrawOk = TRUE;
CRect cRect( 30, 0, 200, 200 );
::InvalidateRect( GetSafeHwnd(), &cRect, FALSE );
}
『 CRect 作って InvalidateRect() の第2引数に渡すわけね』
「そうすると、さっきの領域が」
『値変わった……けどなんか変?』
「まず、 InvalidateRect() の第1引数は」
『 GetSafeHwnd() ……あれ? いつもの m_cCanvasStatic.GetSafeHwnd
じゃない』
「確かに再描画したいのは IDC_S_CANVAS だけど、再描画するコードが入っ
てるのは CAnimeDlg::OnPaint() 、だから再描画の命令は」
『ダイアログの方に送らなきゃいけないんだ!』
「そういうこと。さっき InvalidateRect() 使った時もそうだったけどね」
『気付かなかった……』
「で、この更新領域の指定もダイアログに対してしてるんだけど、更新領域
を調べるのは」
『 BeginPaint() で渡してるのは m_cCanvasStatic.GetSafeHwnd() だから
ダイアログの内側、だからちょっと値が違うんだね。なるほどー』
「あ、あともうひとつ。ボタン押したらどうなる?」
『そりゃもちろんあいうえおって……あれ? なんか左が欠けてる!』
「更新領域の指定、つまり CRect 作った時の値の左上隅のが」
『 left が 30 ってことは、少し右にずれてる……』
「この範囲が〈更新すべき範囲〉だから、逆にその外は〈更新できない範
囲〉になるわけ」
『だから欠けちゃったわけねー』
/*
Preview Next Story!
*/
『再描画されるようになると、ちょっとまともなアプリって感じ』
「ウィンドウを隠すと消えちゃうんじゃね」
『でもまだまだ、普通のアプリと違う感じかなー』
「でもちょっとしたことで見栄えは変わるもんだよ」
『たとえば?』
「たとえば、押すと色が変わるボタンとか」
『あ、それいい感じ!』
「というわけで次回」
< Version 7.17 オーナードローでカッコヨク! >
『につづく!』
「でも、ちょっと寄り道っぽい話だから要らないかなぁ」
『いるいる絶対いるっつーか教えろ!』