お世話になります。質問ばかりで恐縮しますがよろしくお願いします。
環境はWindows 7 MFC MDI です。
現在のView への描画方法ですが、OnPaint()でこのように行なっております
CRect rc;
GetClientRect(&rc); // 描画可能範囲を求める
CPaintDC dc(this); // device context for painting
// 描画用の座標系を設定
dc.SetMapMode(MM_ANISOTROPIC); // マッピングモードを変える
dc.SetWindowExt(1000, 1000); // 仮想的なWindowサイズの指定
GetClientRect(rc);
dc.SetViewportExt(rc.right, -rc.bottom); // ビューポートの指定
dc.SetWindowOrg(0,0); // 仮想座標系の原点
dc.SetViewportOrg(0, rc.bottom); // ビューポートの原点
この設定を元に、
・裏画面用の DC を作成する
・裏画面作成用のビットマップを用意する
・裏画面(dcMem)から表画面(dc)に一気に画像を転送する
この設定で、チャイルドフレーム(実際に描画される部分)にグラフを描画しています。ところが
チャイルドフレームを最大化にしても、グラフの上側(論理座標で900~1000)付近の
表示ができません。チャイルドフレームの最大化を解除して、フレームの上側をマウスでつまんで上に伸ばしてやると、
グラフが正しく書かれていることが確認できます。
Windows XP やVista で実行しても同じ現象です。
フレームを最大化したときに、フレーム内にグラフの原点0,0(左下隅)が、最大値1000,1000(右上隅) 収まるように
するにはどうしたらいいのでしょうか?
そのViewなるものがCViewから派生しているのであれば
OnPaint()の実装は無用です。
CViewの描画はOnDraw()で行わなければなりません。
こんな感じですね。
MyView::OnDraw( CDC *pDC)// 対象DC
{
// 裏画面に描画
DrawGraphAll( m_裏画面MemDC);
// 裏画面を表に転送
m_裏画面MemDC.BitBlt_to( pDC);
}
次に、裏画面を使う場合、OnSizeを実装していると思います。
概ね次のようなコードになっているはずですが、大丈夫ですか。
MyView::OnSize(・・・)
{
CView::OnSize( nType, cx, cy);// 派生元の処理
if( SIZE_MINIMIZED == nType) return;// MINIMIZEなら処理しない
GetClientRect( &m_RcClient);// クライアント矩形を取得
if( TRUE == m_RcClient.IsRectEmpty()) return;
裏画面MemDC.SizeChange( this, m_RcClient); // 裏画面のサイズ変更
Invalidate( NULL, FALSE);// 全体を表示更新
// UpdateWindow(); // 実装によっては、すぐに OnPaint()OnDraw()を実行
}
補足
OnWindowPosChanged()を実装している場合は
OnDrawの「裏画面への描画」はたぶんOnWindowPosChanged()
でやっているでしょうからOnDraw()内では不要ですね。
もちろん、他のタイミングで描画している場合も同様です。
仲澤@失業者 さん。お世話になります。
>そのViewなるものがCViewから派生しているのであれば
>OnPaint()の実装は無用です。ご指摘通りだったのでOnPaint()は削除し
OnDraw(・・・) へ移行しました
>次に、裏画面を使う場合、OnSizeを実装していると思います
行っていませんでいた。見よう見真似でやっているのですが
>裏画面MemDC.SizeChange( this, m_RcClient); // 裏画面のサイズ変更
> Invalidate( NULL, FALSE);// 全体を表示更新
この部分なのですが、意味は理解できたのですが
CDC * pDC = this->GetDC();
/* 裏画面用の DC を作成する */
m_dcMem.CreateCompatibleDC(pDC);
/* 裏画面作成用のビットマップを用意する */
m_bmp.CreateCompatibleBitmap(pDC, m_RcClient.Width(), m_RcClient.Height());
画面をリサイズする関数が分からなかったので、このようにしてみたのですが
BOOL CDC::Attach(HDC hDC)
{
ASSERT(m_hDC == NULL); // only attach once, detach on destroy
・・・・
}
ここに突入してお亡くなりになります。
リサイズ方法をご教授願えないでしょうか。それから、根本的なことになりますが
以下ここら辺の設定は、作り直すといいますが破棄して、OnSize(・・・)などで
リアル座標を取得し、実装するように考え直したほうがよろしいでしょうか。
掲載されているコード自体には問題はないようです。
多分その他の部分に問題があるのでしょう。
まず、裏画面MemDCはクラスにしましょう。以下サンプル。
class 裏画面MemDC: public CDC
{
protected:
CBitmap m_bmp; // 使用
CBitmap * m_bmp_OLD; // 旧
public:
// 初期の作成
void Create( CWnd * ex_Owner, int w, int h)
{
CDC * OwnerDC = ex_Owner->GetDC();
CreateCompatibleDC( m_OwnerDC);
m_bmp.CreateCompatibleBitmap( OwnerDC, w, h);
m_bmp_old = SelectObject( &m_bmp);
}
// リサイズ
void ReSize( CWnd * ex_Owner, int w, int h)
{
SelectObject( &m_bmp_OLD);// 旧に戻す
m_bmp.DeleteObject();//使用したBMPを破棄
DeleteDC(); // 自分を破棄
Create( ex_Owner, w, h);//再作成
}
};
まちがった orz.
× CreateCompatibleDC( m_OwnerDC);
○ CreateCompatibleDC( OwnerDC);
仲澤 さん。お世話になります。
過去ログ( http://rarara.cafe.coocan.jp/cgi-bin/lng/vc/vclng.cgi?
print+200910/09100004.txt)で、
仲澤 さんが今回の事例と同じ回答をされているのを参考に
裏画面MemDC クラスに下記のメンバ関数を追加しました。
/* 後始末 */
void Destroy_Me(){
/* デバイスコンテキスト取得 */
SelectObject(m_bmp_OLD);
/* ビットマップを削除 */
m_bmp.DeleteObject();
/* メモリDCを削除 */
DeleteDC();
}
そして、このようにOnSize()を実装しましたが、結論から申しますと。やはり
論理座標をx 1000 y 1000 とするとグラフの上側(論理座標で900~1000)付近の表示が
できません。
x 900 y 900 ですと表示できますが、他のPC(Laptop PC で画面の縦横比が幅広)だと、
900 900 でも
表示ができません。どこか根本的な間違いをしている気がします。
void CMChartView::OnSize(UINT nType, int cx, int cy)
{
裏画面MemDC cdc;
CView::OnSize( nType, cx, cy); // 派生元の処理
if( SIZE_MINIMIZED == nType) // MINIMIZEなら処理しな
い
return;
GetClientRect( &m_RcClient); // クライアント矩形を取
得
if( TRUE == m_RcClient.IsRectEmpty())
return;
HWND hwnd = GetSafeHwnd(); // ウィンドウの
ハンドル取得
CWnd *pwnd = CWnd::FromHandle(hwnd); // デバイス コンテキストへのハン
ドル
cbs.ReSize( pwnd, m_RcClient.Width(), m_RcClient.Height() );
cbs.Destroy_Me();
Invalidate( FALSE);// 全体を表示更新
UpdateWindow();
CDC *pDc = GetDC();
OnDraw(pDc);
}
ちなみにCView クラスはCScrollView を継承していて
class CxxxView : public CScrollView
OnEraseBkgnd()はこのようにしています。
BOOL CxxxView::OnEraseBkgnd(CDC* pDC)
{
GetClientRect(&m_RcClient);
pDC->FillSolidRect(&m_RcClient, BLACK); // 黒
return TRUE;
}
問題点の場合わけをしましょう。
1.グラフ描画をコメントアウトして、クライアント領域
全体に×を表示してみる。クライアント矩形の頂点に
一辺20[pixl]の□を描画してみる。原点(0,0)にr=10[pixl]の
○を表示してみる。
これらが描画されない場合は
1.1 裏バッファのサイズがおかしい
1.2 Invalidate()している矩形がおかしい。
の可能性があります。
うまく描画できる場合は
1.3 グラフの描画機能がおかしい。
1.4 クリッピング矩形がおかしい。
などですね。
また、OnEraseBkgnd()の塗りつぶし色ですが、
テスト中は、本番色ではなく、赤など、それとわかる
色にしておきましょう。
仲澤さん。お世話になります。
検証したところ、1.1 裏バッファのサイズがおかしい 結論を得ました。ということで調
べていくと、
MSDN のCScrollView::SetScrollSizes
http://msdn.microsoft.com/ja-jp/library/3ew6s3ez(v=vs.80).aspx を読みますと。
「これらのモードはすべて Windows で定義されています。2 つの標準マップ モード
MM_ISOTROPIC と
MM_ANISOTROPIC は、CScrollView では使いません。クラス ライブラリには、ビューをウ
ィンドウのサイズに伸縮する
SetScaleToFitSize メンバ関数が用意されています。・・・」
このような文章が、掲載されています。これはCScrollView クラスを継承したCView クラ
スは標準のマップ モードが使用不可と
いうことだと思うのですが、私の認識にまちがいはないでしょうか?また、
当該掲示板の各ログで「CScrollView で拡大・縮小を実装するには」
http://rarara.cafe.coocan.jp/cgi-bin/lng/vc/vclng.cgi?print+200506/05060041.txt
この文章を読みますと、マッピングモードは機能するような事が、書いてあるようにも、
受け止めれるのですが・・・
いずれにせよ。一度問題を整理する意味で、CView クラス CScrollView を継承した
CView クラスについて
通常のDC への描画、ダブルバッファリングを使った描画
以上四通のテストコードで検証してみたいと思います。
お世話になります、解決できましたのでチェックを入れさせてもらいます。
座標系の指定3種類
論理座標の原点(SetWindowOrg)
物理座標の原点(SetViewportOrg)
仮想的なWindowsサイズの指定(SetWindowExt)
マッピングモード2種類
ISOTROPIC、ANISOTROPIC
描画モード2種類
ノーマル、ダブルバッファリング
MDIにて、これらの設定を組み合わせて、検証できるツールを作成しました、それにより
Windowsの描画の仕組みを正しく理解し、描画することができました。
うまく行かなった原因は、座標系の指定とダブルバッファリングの関係に誤解があり
不適切な設定を行っていたため、正しく動作していませんでした。
仲澤 さんありがとうございました。