MDIで描画したグラフの上部が消えている

[戻る]
あられ 2012/02/07(火) 20:21:02
 お世話になります。質問ばかりで恐縮しますがよろしくお願いします。
環境は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(右上隅) 収まるように
するにはどうしたらいいのでしょうか?

仲澤@失業者 2012/02/08(水) 10:27:33
その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()を実行
}

仲澤@失業者 2012/02/08(水) 10:35:51
補足
OnWindowPosChanged()を実装している場合は
OnDrawの「裏画面への描画」はたぶんOnWindowPosChanged()
でやっているでしょうからOnDraw()内では不要ですね。
もちろん、他のタイミングで描画している場合も同様です。

あられ 2012/02/08(水) 15:53:08
仲澤@失業者 さん。お世話になります。
>その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(・・・)などで
リアル座標を取得し、実装するように考え直したほうがよろしいでしょうか。

仲澤@失業者 2012/02/08(水) 16:23:01
掲載されているコード自体には問題はないようです。
多分その他の部分に問題があるのでしょう。

まず、裏画面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);//再作成
  }
};

仲澤@失業者 2012/02/08(水) 16:36:18
まちがった orz.
×    CreateCompatibleDC( m_OwnerDC);
○    CreateCompatibleDC( OwnerDC);

あられ 2012/02/09(木) 19:31:20
仲澤 さん。お世話になります。
過去ログ(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;
}

仲澤@失業者 2012/02/10(金) 09:48:39
問題点の場合わけをしましょう。

1.グラフ描画をコメントアウトして、クライアント領域
 全体に×を表示してみる。クライアント矩形の頂点に
 一辺20[pixl]の□を描画してみる。原点(0,0)にr=10[pixl]の
 ○を表示してみる。

これらが描画されない場合は

 1.1 裏バッファのサイズがおかしい
 1.2 Invalidate()している矩形がおかしい。

の可能性があります。
うまく描画できる場合は

 1.3 グラフの描画機能がおかしい。
 1.4 クリッピング矩形がおかしい。

などですね。
また、OnEraseBkgnd()の塗りつぶし色ですが、
テスト中は、本番色ではなく、赤など、それとわかる
色にしておきましょう。

あられ 2012/02/11(土) 10:50:00
仲澤さん。お世話になります。
検証したところ、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 への描画、ダブルバッファリングを使った描画
以上四通のテストコードで検証してみたいと思います。

あられ 2012/02/19(日) 09:41:30
[[解決]]
お世話になります、解決できましたのでチェックを入れさせてもらいます。
座標系の指定3種類
論理座標の原点(SetWindowOrg)
物理座標の原点(SetViewportOrg)
仮想的なWindowsサイズの指定(SetWindowExt)
マッピングモード2種類
ISOTROPIC、ANISOTROPIC
描画モード2種類
ノーマル、ダブルバッファリング
MDIにて、これらの設定を組み合わせて、検証できるツールを作成しました、それにより
Windowsの描画の仕組みを正しく理解し、描画することができました。
うまく行かなった原因は、座標系の指定とダブルバッファリングの関係に誤解があり
不適切な設定を行っていたため、正しく動作していませんでした。
仲澤 さんありがとうございました。

古いスレッドには追加発言できません。