#pragma twice

KAB-studio > プログラミング > #pragma twice > 136 Version 7.16 再描画させる!

#pragma twice 136 Version 7.16 再描画させる!

前のページへ 表紙・目次へ 次のページへ

 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 オーナードローでカッコヨク! >
につづく!
でも、ちょっと寄り道っぽい話だから要らないかなぁ
いるいる絶対いるっつーか教えろ!
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。