- ベストアンサー
MFCタブコントロールのグラデーション描画
- VC++2010・MFC環境でMFCのタブコントロールのウィンドウ部をグラデーション描画したい。現在の描画ではタブ切り替え時にちらつきが発生している。
- ソースコードにはタブ切り替え時の処理とウィンドウのアクティブ/非アクティブ時の処理が含まれている。
- ちらつきの原因と対処方法、または別の方法について教えて欲しい。
- みんなの回答 (7)
- 専門家の回答
質問者が選んだベストアンサー
クラスビューでCTabXxxCtrlを選んでプロパティを開きます。 プロパティにアイコンでメッセージというのがあるので、そこでWM_PAINTを選べばOnPaint()が作成されます。 どういう事に成るかは作って見れば分かりますが、ここで描画すればチラつくことはなくなると思いますが以前に書いた通りすごく面倒です。 >この場合はタブ・ボタンも全て自分で書く必要があります。 書くではなく正確に言うと描くですね。失礼しました。 OnPaint()を作ってみれば分かります。 どうやって描くかというとCDCクラスのメンバ関数を駆使して下さいとしか言えません。すごく面倒ですけどね。 なお、C++の勉強はMFCをやる以上は避けられないので入門書を購入して勉強してくださいとしか言えません。
その他の回答 (6)
- zwi
- ベストアンサー率56% (730/1282)
見てみたところ問題点はCTabGradDlg::OnDrawItemで強引に描画している所の様です。 m_xcTabの宣言が CTabCtrl m_xcTab; に成っていると思いますが、CTabCtrlを継承したクラスを新規に作成してそのクラス名としてください。 CTabXxxCtrl m_xcTab; と宣言に書き換えます。CTabXxxCtrlはCTabCtrlを継承した新規のクラスです。 その新たなクラスであるCTabXxxCtrlクラスのOnPaint()でオーナードローします。この場合はタブ・ボタンも全て自分で書く必要があります。これは実に面倒なので私としては前に書いたタブボタンだけを使う方法をお勧めします。 継承が分からない事言うことでしたらC++をちゃんと勉強して下さい。C言語の知識だけではMFCを扱えません。
お礼
ありがとうございます。 >CTabCtrlを継承したクラスを新規に作成してそのクラス名としてください。 >CTabXxxCtrl m_xcTab; >と宣言に書き換えます。CTabXxxCtrlはCTabCtrlを継承した新規のクラスです。 >その新たなクラスであるCTabXxxCtrlクラスのOnPaint()でオーナードローします。 CTabCtrlのクラスを作成し、宣言の書き換えはできました。 しかし、『CTabXxxCtrlクラスのOnPaint()でオーナードローします。』がよくわかりません。 クラスを作成しただけではOnPaint()は存在しませんよね? メッセージマップ(?)にも何も書いてないし。。。自分でOnPaint()作ればいいのでしょうか? また、『OnPaint()でオーナードローする。』とはどういうことでしょうか? >この場合はタブ・ボタンも全て自分で書く必要があります。 タブ・ボタンも全て自分で書くというのは、どういうことでしょうか? 自力で調べたりしましたが、未だに『書く』といったことが何を指しているのかよくわかりません。。。 >継承が分からない事言うことでしたらC++をちゃんと勉強して下さい。C言語の知識だけではMFCを扱えません。 そうですね、その通りです。ご不便をおかけしてすみません。 以前に少しだけ習ったのですが、習ってからだいぶ期間が開いていたので、ほとんど忘れてしまいました。
- zwi
- ベストアンサー率56% (730/1282)
次々新事実が出てくるのでソースコードを添付した貰った方が良いと思います。 勘だけで答えるのは限度があります。
お礼
ありがとうございます。 >次々新事実が出てくるのでソースコードを添付した貰った方が良いと思います。 そうですね。何分、初めて質問投稿させていただいたので、何をどう伝えたらいいかわかりませんでした。すみません。 以下、最初の質問以外のソースコードです。(OnPaintとオーナードローの描画についてのみ) void CTabGradDlg::OnPaint() { CDialog::OnPaint(); CTabCtrl *pTabCtrl =(CTabCtrl *)GetDlgItem(IDC_TAB); //--------------------------------- // デバイスコンテキスト(DC)作成 //--------------------------------- CWnd* pWnd = GetDlgItem(IDC_TAB); CDC* dc = pWnd->GetDC(); //--------------------------------- // ダミーデバイスコンテキスト(DC)作成 // ビットマップ作成 //--------------------------------- CDC dcDmy; dcDmy.CreateCompatibleDC(dc); CBitmap bmpDmy; //--------------------------------- // クライアント領域取得 //--------------------------------- CRect rect; GetClientRect(&rect); // クライアント領域の座標取得 pTabCtrl->AdjustRect(FALSE, &rect); //--------------------------------- // 裏描画画面作成 //--------------------------------- bmpDmy.CreateCompatibleBitmap(dc, rect.Width(), rect.Height()); dcDmy.SelectObject(&bmpDmy); //--------------------------------- // 256色の配列レコード作成 //--------------------------------- int segments = 255; DWORD* dwBits = new DWORD[segments]; for(int i = 0; i < segments; ++i) { dwBits[i] = (DWORD)RGB(0xFF - i, 0xFF - i, 0xFF - i); }; //--------------------------------- // 32ビットの1ピクセル幅ビットマップ // と255ピクセル作成 //--------------------------------- HBITMAP hBitmap = ::CreateBitmap(1, segments, 1, 32, dwBits); delete [] dwBits; //--------------------------------- // パターンブラシ作成(グラデーション) //--------------------------------- CBrush brush; brush.CreatePatternBrush(CBitmap::FromHandle(hBitmap)); CBrush* oldbrush = (CBrush*)dcDummy.SelectObject(&brush); //--------------------------------- // パターンブラシ作成(フレーム) //--------------------------------- CBrush frmbrush; frmbrush.CreateSolidBrush(RGB(255, 255, 255)); //--------------------------------- // パターンブラシで四角形描画 //--------------------------------- CRect frmrect; frmrect = rect; frmrect.top = 0; frmrect.left = 0; dcDummy.Rectangle(frmrect); dcDummy.FrameRect(frmrect, &frmbrush); dcDummy.SelectObject(oldbrush); //--------------------------------- // ブラシの破棄 //--------------------------------- frmbrush.DeleteObject(); // ブラシの破棄 brush.DeleteObject(); // ブラシの破棄 //--------------------------------- // 貼り付け //--------------------------------- dc->BitBlt(rect.left , rect.top , rect.Width(), rect.Height(), &dcDmy, 0, 0, SRCCOPY); //--------------------------------- // メモリの解放 //--------------------------------- dcDmy.DeleteDC(); bmpDmy.DeleteObject(); pWnd->ReleaseDC(dc); //CDCを解放 } //************************************************************** // TabGradDlgのオーナードロー //************************************************************** void CTabGradDlg::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpdis) { if(lpdis->CtlType == ODT_TAB) { CTabCtrl *pTabCtrl = (CTabCtrl *)GetDlgItem(IDC_TAB); //--------------------------------- // デバイスコンテキスト(DC)作成 //--------------------------------- CDC *dc = CDC::FromHandle(lpdis->hDC); ・・・・ // 以下、OnPaint()と同様 ・・・・ //--------------------------------- // タブの文字列表示(ブラシ破棄後実装) //--------------------------------- TC_ITEM tcItemGet; TCHAR buffer[256] = {0}; tcItemGet.pszText = buffer; tcItemGet.mask = TCIF_TEXT; tcItemGet.cchTextMax = 256; m_xcTab.GetItem(lpdis->itemID, &tcItemGet); CRect Rect = lpdis->rcItem; Rect.left += 3; Rect.top += 3; dc->DrawText(tcItemGet.pszText, -1, Rect, DT_END_ELLIPSIS); ・・・ // 貼りつけ、メモリ解放 } } あと、他のソースコードは、質問に添付してあったOnActivateとOnTcnSelchaneTabです。 それ以外は、特にコードを書いていません。 宜しくお願いします。
- zwi
- ベストアンサー率56% (730/1282)
試して見ましたが、CDialogを継承したCXXXDlg上でOnPaint()で描画したものはチラつきません。タブコントロールは、CXXXDlgのダイアログ上に有るものとします。 普通にやるとタブコントロールがタブ以外の部分も白く描画してしまうので、タブボタンを残してリソースエディタ上でサイズを縮めてやりました。 こういう状態では無いんですよね? あとの方法は、CTabCtrlクラスを継承して継承クラスのOnPaint()でオーナードローする方法ですがタブボタンまで自分で書かなくてはいけないので面倒です。
お礼
ありがとうございます。 >普通にやるとタブコントロールがタブ以外の部分も白く描画してしまうので、タブボタンを残してリソースエディタ上でサイズを縮めてやりました。 >こういう状態では無いんですよね? 『タブボタンを残して』の意が読み取れませんでした。 リソースエディタ上でタブコントロールのサイズを小さくするといったことでいいんですかね? そうだとすると、そういう状態ではないです。 >あとの方法は、CTabCtrlクラスを継承して継承クラスのOnPaint()でオーナードローする方法ですがタブボタンまで自分で書かなくてはいけないので面倒です。 『CTabCtrlクラスを継承して継承クラスのOnPaint()でオーナードロー』とはどういった方法なのでしょうか? 記述し忘れていましたが、現在、タブコントロールはオーナードローにしていて、オーナードロー内でもグラデーションの描画をしています。 なので、《オーナードローで描画》・《OnPaintで描画》するようになっています。
- zwi
- ベストアンサー率56% (730/1282)
すいません状況を整理させて下さい。 まとめると ・タブコントロールは使っているが、各タブ用の子ダイアログは用意していない。 ・OnPaintでグラデーション背景を描いている。 ・OnActiveでInvalidate();して再描画を行わないと再描画されずにウィンドウ画像が崩れる場合がある。 ってことですよね? このOnPaintとOnActiveがあるのはCTabGradDlgですか?で、このCTabGradDlgは何を継承したクラスなのでしょうか?プログラムの作りそのものに問題がある気がしてきました。 各タブ用の子ダイアログを作って各子ダイアログでグラデ背景を描画すれば解決する気がします。
お礼
ありがとうございます。 >・タブコントロールは使っているが、各タブ用の子ダイアログは用意していない。 その通りです。 >・OnPaintでグラデーション背景を描いている。 OnPaintの他にタブコントロールをオーナードローしているので、そこでもグラデーション描画をしています。 また、背景というより、DCに描画しているような感じです。 >・OnActiveでInvalidate();して再描画を行わないと再描画されずにウィンドウ画像が崩れる場合がある。 『ウィンドウ画面が』というより、『タブコントロールの一部のが』といった感じです。(同じですかね?) >このOnPaintとOnActiveがあるのはCTabGradDlgですか? はい、同じCTabGradDlgです。 >で、このCTabGradDlgは何を継承したクラスなのでしょうか? CDialogEx(CTabGradDlg::IDD, pParent)だと思います。 (いまいち継承の概念が読み込めていないので曖昧です。すみません。) >各タブ用の子ダイアログを作って各子ダイアログでグラデ背景を描画すれば解決する気がします。 その方法も考えたのですが、そうすると、子ダイアログの大きさが統一できないのではないかと思いまして。。 また、その方法だと、子ダイアログの背景をグラデーションにし、子ダイアログにそれぞれボタン等のコントロールを表示するという風になるのでしょうか?
- zwi
- ベストアンサー率56% (730/1282)
失礼しましたOnPaintで書いているんですね。OnPaintでInvalidate();すると描画が無限ループになるのでやってはいけません。 ところでWA_ACTIVEやWA_CLICKACTIVEでInvalidate();は必要なのでしょうか?そもそもOnActivate自体いらないのでは?OnActivateが必要になった事ってあまり無いですよ。
お礼
ありがとうございます。 >ところでWA_ACTIVEやWA_CLICKACTIVEでInvalidate();は必要なのでしょうか? ウィンドウの一部(下記のタブの【*****】部分)が、他のウィンドウの影になって再度ウィンドウをアクティブにした時に、(タブの)影になっていた部分が描画されていなかったので、 ウィンドウがアクティブになったら再度描画しよう。という結果になり、このようなコードになりました。 ______ | タブ1 | タブ2|________ | **************| | **************| | **************|  ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ >そもそもOnActivate自体いらないのでは?OnActivateが必要になった事ってあまり無いですよ。 上記にも説明した理由の為OnActivateを使用していましたが、OnActivateを使用する以外で他の方法があるのでしょうか?
- zwi
- ベストアンサー率56% (730/1282)
MFCでダイアログ系(タブもダイアログ系)の場合は、OnPaintで描画しないとちらつきますが大丈夫でしょうか?
お礼
回答ありがとうございます。 >OnPaintで描画しないとちらつきますが大丈夫でしょうか? というのは、OnPaintでInvalidateを実装(?)すればいいのでしょうか? OnPaintでは現在、ダブルバッファリングにてグラデーションの描画をしています。 また、補足としては、タブはダイアログではなく、タブコントロール貼りつけたまま(言い方変?) の状態で、タブ遷移することで、子ダイアログが表示されるといったものではありません。
お礼
ありがとうございます。 >クラスビューでCTabXxxCtrlを選んでプロパティを開きます。 >プロパティにアイコンでメッセージというのがあるので、そこでWM_PAINTを選べばOnPaint()が作成されます。 >どういう事に成るかは作って見れば分かりますが、ここで描画すればチラつくことはなくなると思いますが以前に書いた通りすごく面倒です。 上記手順で作成したら、作成できました。ありがとうございます。 OnPaintに何もコードを書かずにデバッグ確認したら、タブすらも表示されませんでした。 それが >>この場合はタブ・ボタンも全て自分で書く必要があります。 ということですね? >なお、C++の勉強はMFCをやる以上は避けられないので入門書を購入して勉強してくださいとしか言えません ネットに転がっているものを参考に勉強するだけでは、不十分なのでしょうか? 当初の目的はグラデーション描画についてのことでしたので、ベストアンサーを付けさせていただきます。