#pragma twice

KAB-studio > プログラミング > #pragma twice > 135 Version 7.15 再描画できるように

#pragma twice 135 Version 7.15 再描画できるように

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

 Version 7.15
再描画できるように

ごめんなさい!
な、なに?
これまでのプログラムでミスがありました。まずその訂正から
行き当たりばったりでやってるから……
う”。えーっと、これまでウィンドウのデバイスコンテキストを GetDC() 
で取得してました。これはいいんですが、実は、これを呼んで描画したあと
は ReleaseDC() という API を呼ぶ必要がありました
げ、そなの?
そうなの。 GetDC() 読めば載ってるんだけど、気付かなくて……という
わけで、 Ver 7.01 ( No.121 ) なら

void CAnimeDlg::OnBDraw() 
{
    COLORREF dwColor = RGB( 255, 0, 0 );
    HDC hDC = ::GetDC( m_cCanvasStatic.GetSafeHwnd() );
    ::SetPixel( hDC, 10, 10, dwColor );
    // 下の1行を追加。
    ::ReleaseDC( m_cCanvasStatic.GetSafeHwnd(), hDC );
}

って感じに、後片付けをする部分で ReleaseDC() を呼んでください。第
1引数に GetDC() に渡したウィンドウハンドル、第2引数に GetDC() の戻
り値として返ってきたデバイスコンテキストのハンドルを渡してください
後片付けってことは、最後に元のペンとかに SelectObject() した後に呼
べばいいんだよね
そういうこと。ま、小規模なアプリならこれくらい問題ないけど、描画処
理を長時間行うアプリとかだと問題が出るかもしれないから忘れないでね
で、今日は?
今日は〈再描画〉について
再び描く、ってことよね
文字通りならね。でもむしろ、本当は〈描画せずに再描画だけする〉って
いうのが普通の処理
???
うん、この辺の考え方が難しいかもね。まずは、 Ver 7.12 ( No.132 ) 
で教えた文字列の描画から。あ、前回の CAnimeDlg::OnInitDialog() の部
分はナシにしてね

void CAnimeDlg::OnBDraw() 
{
    HDC hCanvasDC = ::GetDC( m_cCanvasStatic.GetSafeHwnd() );
    const char pchText[] = "あいうえお";
    ::TextOut( hCanvasDC, 0, 0, pchText, strlen( pchText ) );
    ::ReleaseDC( m_cCanvasStatic.GetSafeHwnd(), hCanvasDC );
}

 ReleaseDC() が入ってる以外は同じだけど
そう同じ。別に文字列じゃなくても、 SetPixel() でもなんでもいいんだ
けど分かりやすそうだし簡単だからこの例。で、この文字列を描画して
描画して
この【Anime】ダイアログを別のウィンドウで隠して
隠して
もう一度表示すると
げ! 文字列が消えてる! なんで?
これが再描画。ウィンドウが〈隠れてまた表示される〉時には、もう一度
ウィンドウ全体が描画されなおすんです。でもこの文字列はボタン押した時
にしか描かれないから
消えちゃうってゆーの!? なんか変! 一度描いたら、それが残ってて
もいいじゃん
確かにねー。この辺はデバイスコンテキスト、というよりは、画面の仕組
みの問題かも
画面の仕組み?
画面にいっぱいウィンドウあるけど、描画する時は、ウィンドウごとじゃ
なくて、画面全体って形になるんだよね
そういえばクリッピングしないとはみでちゃうけど、それと同じ?
そうそう。感覚的にはウィンドウが〈重なってる〉ように見えるけど、そ
れはそう見せかけてるだけ。ウィンドウを描画する時は画面に直接描いてる
わけ。立体的に見えるけど、実は平面のキャンバス一枚、って言った方がい
いかも
だから消えちゃう?
〈ダイアログに描いた〉って思っても実際には画面に描いただけ。だから
それが〈ダイアログに描いた〉ってふうにウィンドウズシステムには伝わっ
てないから
描いたこと憶えてくれない
後ろに隠れた時にも〈元々ダイアログに描かれてたもの〉とか憶えてもら
えてないしね。今の〈あいうえお〉って描いた部分、半分だけ他のウィンド
ウで隠してみると
隠したとこだけ消えちゃうね
上に乗っけたウィンドウを描画しちゃうから、今の文字列の部分が消え
ちゃう。で、そこは憶えてもらってないから
消えちゃうのねー
あと、これを試してみようか

void CAnimeDlg::OnBDraw() 
{
    // 画面のデバイスコンテキストを取得。
    HDC hCanvasDC = ::GetDC( NULL );
    const char pchText[] = "あいうえお";
    ::TextOut( hCanvasDC, 30, 30, pchText, strlen( pchText ) );
    ::ReleaseDC( NULL, hCanvasDC );
}

ん? ほとんど変わらないけど
とりあえず試してみて
ほい実行。げ! 画面左上にあいうえおが!
 GetDC() にはこれまでウィンドウハンドルを渡してたけど、これに NULL 
を渡すと画面全体のデバイスコンテキストが取得できます
画面全体……
むしろ、ウィンドウズシステムはこれにだけ描画してるって考えた方がい
いかもね
はー。なんかすごいかも
というわけで、一度隠れると消えちゃう理由は分かったと思います
不便なのに代わりはないけどね。っつーか、どうやって解決するの?
そこがポイント。〈ボタンが押された〉時に CAnimeDlg::OnBDraw() が呼
ばれるように、再描画が必要な時に関数が呼ばれれば
そこで描画すればいい!
そういうこと。これが再描画。〈さっき描画したのを描画して〉ってウィ
ンドウズが求めてきた時に、それに応えればいいわけ
で、それはどうやって?
実はその関数は元々用意されてるんです。 AnimeDlg.cpp の真ん中あたり


void CAnimeDlg::OnPaint() 
{
    if (IsIconic())
    {
        CPaintDC dc(this); // 描画用のデバイス コンテキスト

        SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

        // クライアントの矩形領域内の中央
        int cxIcon = GetSystemMetrics(SM_CXICON);
        int cyIcon = GetSystemMetrics(SM_CYICON);
        CRect rect;
        GetClientRect(&rect);
        int x = (rect.Width() - cxIcon + 1) / 2;
        int y = (rect.Height() - cyIcon + 1) / 2;

        // アイコンを描画します。
        dc.DrawIcon(x, y, m_hIcon);
    }
    else
    {
        CDialog::OnPaint();
    }
}

あるある! これがそうなの?
そう。たとえば

void CAnimeDlg::OnPaint() 
{
    if (IsIconic())
    {
// 略。
    }
    else
    {
        TRACE( "再描画!\n" );
        CDialog::OnPaint();
    }
}

 TRACE() を入れてみるわけね
これでさっきみたいに隠してみれば
表に出すたびに〈再描画〉って出るね。ここでボタン押した時と同じよう

やっちゃダメ
げ、そなの?
最初のごめんなさいと関係するのはこの辺。このときには、描画する時に
ルールがあるんです。ま、それはソースを見てもらってってことで

void CAnimeDlg::OnPaint() 
{
    if (IsIconic())
    {
// 略。
    }
    else
    {
        // 追加、ここから。
        PAINTSTRUCT stPaintStruct;
        HDC hDC
             = ::BeginPaint
                    ( m_cCanvasStatic.GetSafeHwnd()
                    , &stPaintStruct 
                    );

        const char pchText[] = "あいうえお";
        TextOut( hDC, 0, 0, pchText, strlen( pchText ) );

        ::EndPaint
            ( m_cCanvasStatic.GetSafeHwnd()
            , &stPaintStruct 
            );

        // 追加ここまで。
        CDialog::OnPaint();
    }
}

 API 的には BeginPaint() と EndPaint() ね
この再描画時には、 GetDC() と ReleaseDC() の代わりに BeginPaint() 
と EndPaint() を呼ぶって憶えれば大丈夫かな
 PAINTSTRUCT は?
これは構造体で、描画する時の情報が入ってるんだけど、特に重要なもの
は入ってないから放っといていいかも
で、これで挟む他はいつも通りでいいんだよね
そゆこと。 BeginPaint() の戻り値に描くためのデバイスコンテキストが
返ってくるから、それを使って描画すればOK

/*
    Preview Next Story!
*/
あれ? でもこれなんか……
ぎく!
……なーんかアヤシイわねー
……僕だって分からないことや知らないことも結構あるんだから!
げ、開き直りやがったこいつ!
というわけで次回
< Version 7.16 再描画させる! >
につづく!
でも火美ちゃんよりはずっとずっと知ってるんだけどねー
がーっムカツク!!
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。