ボタンを再描画するには

解決


鈴木  2003-02-06 00:51:59  No: 51019  IP: [192.*.*.*]

何度も助けてもらいながらウィンドウにボタンを配置するまではできました。
今度はアクティブなウィンドウを切り替えて、作ったウィンドウが隠れた状態になってから、再び作ったウィンドウをアクティブにするとボタンのところだけが再描画されずに後ろの(一時的にアクティブだった)ウィンドウが残ってしまいます。
ウィンドウを作るときに「WS_CLIPCHILDREN」を入れていなければ問題なくボタンも再描画されるのですが、ウィンドウの背景に画像を表示していて入れないわけにはいかないので「WS_CLIPCHILDREN」をいれてウィンドウを作っています。
「WM_PAINT」メッセージを受けて「InvalidateRect(ボタンのハンドル, NULL, TRUE);」で再描画などと考えましたが、ボタンが1つならこれでも可能かもしれませんが、複数個ある上に間違ったままも良くないと思い質問させていただきました。
どのようにすればよいのか、回答お願いします。
環境:Windows2000、C++6(SP5)、SDK、SDI

編集 削除
YuO  2003-02-06 02:53:21  No: 51020  IP: [192.*.*.*]

こちらでは再現できないのですが……。
↓とりあえず,テスト用のサンプルプログラム。
http://www.246.ne.jp/~y-ookubo/ButtonSample.lzh
#VC++用です。makefile書き換えれば他のコンパイラでも大丈夫かと。

適当な部分の抜粋があれば回答できるかもしれませんが……。

Windows NT 4.0 Workstaion (with Service Pack 6a) for NEC
Visual C++ 5.0 (with Service Pack 5)

編集 削除
鈴木  2003-02-06 03:54:14  No: 51021  IP: [192.*.*.*]

早速の回答ありがとうございました。
空のプロジェクト(Win32 Appliction)を作成してサンプルプログラムを実行してみました。
結果は、ウィンドウの背景が後ろのウィンドウになってしまっていてウィンドウを移動させてもその状態を保持していました。ただし、ボタンとタイトルバーだけは正常に描画されているようです。
適当な部分がどのあたりかすらわからないのですが、ボタンを作っているところでしょうか。

「WM_CREATE」でウィンドウのサイズ変更・場所移動・アイコンとタイトルの変更、ボタンの表示等の処理をさせているのですが、これがいけないのでしょうか。
ちなみに、起動時にファイルからウィンドウのタイトル・アイコン・サイズ・位置、ボタンのサイズ・位置・キャプション・数を読み込んで自身をその通りの状態にして表示させるようなものを作成しようとしています。

編集 削除
YuO  2003-02-06 12:47:08  No: 51022  IP: [192.*.*.*]

> 結果は、ウィンドウの背景が後ろのウィンドウになってしまっていてウィンドウを移動させてもその状態を保持していました。

それは,背景をどのように描画させた場合ですか?
私の環境では,全てのファイルを同一ディレクトリに置いた状態でnamkeすれば,
どの方法で描画させてもちゃんと背景が表示されましたが。

それでもって,どこに問題があるかは調べなかったのですか?
動くと言っている物が動いていないのですから,
デバッガを起動して調べてみればそれくらいはわかるでしょう?


> 適当な部分がどのあたりかすらわからないのですが、ボタンを作っているところでしょうか。

関係しそうな部分のことです。
まずは,ウィンドウにボタンを一つ作成し,背景を描画してみて,
ちゃんと動くプログラムを作るところから始めてみてください。
#それが,「適当な部分」フルセットになります。

編集 削除
鈴木  2003-02-07 01:05:47  No: 51023  IP: [192.*.*.*]

> それでもって,どこに問題があるかは調べなかったのですか?

申し訳ありませんでした。他人任せになりすぎでした。
改めて調べてみるとリソースのビットマップが読み込めていなかったようです。
いろいろ本やWebサイトを見て挑戦しているのですが、いまだにリソースの扱い方がいまいちわかっていないので、自分でわかる方法でやってみようと直接ビットマップを読み込ませて実行してみると、ちゃんと背景も表示されて正常と思える動作をしました。

自分で作成している方は提示していただいたサンプルと基本的には同じようにウィンドウの作成やボタンの作成を行っていると思うのですが、やはりうまくいきませんでした。(ウィンドウのスタイルは違いますが)
ただ単純にウィンドウを作成して背景を描画し、ボタンを配置するだけのものにまで削ってみたのですがそれでもボタンの再描画がされずに後ろのウィンドウを残したままになってしまいました。
http://www.geocities.co.jp/SiliconValley-Oakland/1272/test2.htm
これがそのプログラムのcppファイルの中身です。
ここから更に「WM_PAINT」の処理を全てなくすとボタンの再描画は正常に行われるようになりました。
そこでここに原因があるのではないかと思うのですが、どうでしょうか。
hBitmapにはロードしたビットマップのハンドルが入っています。
1枚のビットマップをファイルから直接ロードしてそのままウィンドウの背景として表示させています。

BITMAP BMPINFO;
HDC hMem, hDC;
if (hBitmap!=NULL)
{
  hDC=GetDC(hwnd);
  GetObject(hBitmap, sizeof(BITMAP), &BMPINFO);
  hMem = CreateCompatibleDC(hDC);
  SelectObject(hMem, hBitmap);
  BitBlt(hDC, 0, 0, BMPINFO.bmWidth, BMPINFO.bmHeight, hMem, 0, 0, SRCCOPY);
  DeleteDC(hMem);
  ReleaseDC(hwnd, hDC);
}

編集 削除
鈴木  2003-02-07 01:10:11  No: 51024  IP: [192.*.*.*]

すみません。ちょっと違いました。
「WM_PAINT」自体をなくさないとこれまでと同様にボタンの再描画はされないままでした。
switch文の「case WM_PAINT:」そのものを全てコメントアウトして削除するとボタンの再描画は正常に行われるのですが、「WM_PAINT」の中身だけを全てコメントアウトして削除してもボタンの再描画はされませんでした。

編集 削除
YuO  2003-02-07 10:02:07  No: 51025  IP: [192.*.*.*]

> 改めて調べてみるとリソースのビットマップが読み込めていなかったようです。

う〜〜ん,全てのファイルを同一ディレクトリに置いて,
そのディレクトリでnamkeすれば問題なく作成できるはずなのですが……。
#基本的に,namkeで一気に作成してしまうことを前提にしています。


> ここから更に「WM_PAINT」の処理を全てなくすとボタンの再描画は正常に行われるようになりました。

そりゃ,WM_PAINTでGetDCしてりゃ……。
こちらでもGetDC使って追試してみたところ,ボタンは描画されませんでした。

私の書いたWM_PAINT応答をよく見て下さい。
また,私の書いたプログラムの11行目を
>#define BKGND_TYPE           BKGND_USE_WNDCLASS
から
>#define BKGND_TYPE           BKGND_USE_PAINT
にして処理してみて下さい。たぶん,問題なく動くでしょう。

とりあえず,先ほど書いたURLのファイルに対して
・デフォルトの描画方法をWM_PAINT応答に変更 [ test.c ]
・VC++ 5.0(SP3)で生成した実行ファイルを追加 [ test.exe ]
という変更を加えておきました。
この実行ファイルを実行してみて下さい。

編集 削除
鈴木  2003-02-07 14:20:50  No: 51026  IP: [192.*.*.*]

ありがとうございました。
MSDNで見てみたのですが「nmake」「makefile」の使い方がいまいちよくわからないので、ゆっくり調べてみます。
「test.c」だけプロジェクトに挿入して「F5」でビルドしていたのですが、これだとリソースちゃんと使えないんですね。当たり前だと言われるかもしれませんが、今後の学習課題として更に調べて試してみようと思います。
サンプルに付けてくださっていた「test.exe」の方は正常に背景もボタンも描画されています。

「GetDC」使っての方法しかわからなかったので使っていました。
「BiginPaint」の説明を見てみたのですが英語しかないので完全には読めませんが、取得するデバイスコンテキストから自動的に除外してくれるとかそんな感じで書いてあるのはわかります。つまり、書き換えの不必要な領域を除外してくれていると言うことでいいんですよね。
「BiginPaint」「EndPaint」を使う方法に変えるとうまくいきました。
また、「GetDC」を使った場合でいろいろやってみたのですが、WM_PAINTの最後に「return(DefWindowProc(hwnd, uMsg, wParam, lParam));
」を追加するとまともっぽく描画できました。ただし、ウィンドウを移動させたときになどに残像が残ってしまうほど処理は遅いですが。

YuOさんのおかげでなんとか無事解決できました。
ありがとうございました。

編集 削除
YuO  2003-02-07 15:57:06  No: 51027  IP: [192.*.*.*]

> MSDNで見てみたのですが「nmake」「makefile」の使い方がいまいちよくわからないので、ゆっくり調べてみます。

nmake(またはmake)というのは,更新日付をチェックして,
作りたいファイル(ターゲットファイル)が元となるファイル(依存ファイル)より古い場合,
指定の処理を行ってくれる物です。
「ビルド」と呼ばれる処理とほとんど同じです。

今回のようにmakefileがあるなら,
makefileのあるディレクトリに移動して,
>(VC++をインストールしたディレクトリ)\bin\vcvars32
>nmake
と入力してやればターゲットが出来上がります。


まぁ,今回の場合,やっていることは,
cl /c test.c
rc test.rc
link /SUBSYSTEM:WINDOWS /MACHINE:IX86 /ENTRY:WinMain test.objtest.res user32.lib gdi32.lib kernel32.lib
で終わりですが。
#linkの行が長くなるのでレスポンスファイルtest.lrfを利用した。

チョコチョコっとサンプル書いてテストする場合,Developer Studio起動させてやるよりも,
コマンドラインからやってしまった方が楽だったりします。
これを期にコマンドラインコンパイラの使い方も覚えてみるとよいでしょう。


> 「BiginPaint」の説明を見てみたのですが英語しかないので完全には読めませんが、取得するデバイスコンテキストから自動的に除外し てくれるとかそんな感じで書いてあるのはわかります。
> つまり、書き換えの不必要な領域を除外してくれていると言うことでいいんですよ ね。

そういうことです。
ウィンドウの後ろになったり,InvalidateRect/InvalidateRgnなどによって無効化された領域のみを書き換えます。
また,EndPaintにて無効化された領域を有効にします。
#B"e"ginPaintですよ。日本語読みにつられるとエラーがでますので……。


> また、「GetDC」を使った場合でいろいろやってみたのですが、WM_PAINTの最後に 
> 「return(DefWindowProc(hwnd, uMsg, wParam, lParam));」
> を追加するとまともっぽく描画できました。
> ただし、ウィンドウを移動させたときになどに残像が残ってしまうほど処理は遅いですが。

http://msdn.microsoft.com/library/en-us/gdi/pantdraw_4k6d.asp
にはGetDCについての言及がないですし,
http://msdn.microsoft.com/library/en-us/gdi/pantdraw_5f6t.asp
には,
>To draw in a window without using a WM_PAINT message,
>the application uses the GetDC or GetDCEx function to retrieve a display device context for the window.
[WM_PAINTメッセージ無しにウィンドウに描画するために,
  アプリケーションはウィンドウの為のディスプレイデバイスコンテキストを取得するために
  GetDCまたはGetDCEx関数を利用する。]
とあります。
#[]内は私の超訳。

というわけで,
・WM_PAINT応答では,BeginPaint/EndPaintを使う
・それ以外のときはGetDC/ReleaseDCを使う
この原則は崩さない方がよいです。

編集 削除
鈴木  2003-02-07 23:09:47  No: 51028  IP: [192.*.*.*]

丁寧に解説を付けていただいて、ありがとうございます。
コマンドラインコンパイラの方も使い方見てみようと思います。
綴り完全に日本語読みそのものになってしまってました(汗

> この原則は崩さない方がよいです。

そのようにします。
これからまだまだいろいろなことに挑戦しようと思うので、少しずつでも習得できるようにがんばってみます。

編集 削除