画面表示をインプリメントしましょう

  

ここに紹介するプログラムは、WIN XP 2003/Windows 7 (32bits) - OS 環境でマイクロソフトのVisual Studio 2010 で作ったものです。
Windows XP/ Windows 7(32bits) での動作は確認済みですが…

 

このようなプログラムは、印刷・画面表示機能が非常に重要です。

しかしコーディングは思うようにうまく出来ないし、生産性が非常に悪いようで…


 

MFCのAppWizardで「印刷および印刷プレビュー」を選定すると必要なコードがインプリメントされます。

しかし、あくまで基本コードだけで、印字の体裁・ページコントロール・ヘッダ・フッターなど実用的なコードにするには相当苦労させられました。

 

まずは、MFCで印字するときの基本的な動きを知る必要があります。基本コードとは、「ファイル」メニューに「印刷」、「印刷プレビュー」、「プリンタの設定」の各コマンドがメニューに追加されます。

 

印刷プレビュー」や「プリンタの設定」機能は、ほとんどそのまま使えますが、それ以外はMFCのコードの動きなどを理解しながら進めなければなりません。

 

プログラム起動直後のCViewの動きは

 

ファイルの読み込み後

 

計算開始

 

印刷プレビューの起動

 

印刷の開始

 

画面表示のためには、派生クラスのOnDrawをオーバーライドしてここに記述します。 印刷やプレビューは、派生クラスのOnPrintをオーバーライドしてここに記述します。

 

上記の OnDraw や OnPrint が呼び出される前に、フレームワークは、OnPrepareDC を呼びます。

ビューをScrollViewなどにした場合画面や印刷の座標などの設定は、派生クラスのOnPrepareDCをオーバーライドして記述しますが、 このオーバーライドした関数の最初で基本クラスのインプリメントを常に呼び出す必要があります。

 

void CAutoACView::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo)

{

         CScrollView::OnPrepareDC( pDC, pInfo );

          省略

}

 

画面への表示で OnPrepareDC が呼び出されたときは、この引数は NULL となります。

この関数が印刷のために呼び出されるときは、引数 pInfo に設定されたページ情報を調べます。

 

ここでわかる様に、計算データのモニターに表示する座標はOnUpdate 又は OnDraw で設定しなければなりません。 モニターに表示する際、印刷と違いページを決める必要はありませんが、データの長さでスクロールする必要生じます。

 

まず標準の1ページのサイズ(m_rectMonitor)を決めますが、最初に呼ばれるCAutoACView::OnPrepareDC関数に論理Twipsと呼ばれる特別なマッピングモードを設定しました。

このモードは、オペレーティングシステムやディスプレイの解像度から独立しており、Microsoft Wordなどのプログラムで使われているものです。

 

///////////////////////////////////////////////

// DCをセット

void CAutoACView::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo)
{

   pDC->SetMapMode( MM_ANISOTROPIC );

   pDC->SetWindowExt( 1440, 1440 );
   pDC->SetViewportExt( pDC->GetDeviceCaps( LOGPIXELSX ),
                                  -pDC->GetDeviceCaps( LOGPIXELSY ));
   CSize size = pDC->GetWindowExt(); //デバイス コンテキストの大きさ取得
   pDC->DPtoLP( &size ); // デバイス単位を論理単位に変換


    // モニターの表示範囲の初期設定


   if( !pDC->IsPrinting())


      m_rectMonitor = CRect( L_MARGIN, 0, size.cx, -size.cy ); // データを印刷しながらページの最後を判断する


   if( pDC->IsPrinting()){


      if( pInfo->m_nCurPage == (UINT)(m_nMaxPage + 1))


      pInfo->m_bContinuePrinting = FALSE;


   }


}

 

負荷計算やその他の計算を行った後、結果を画面に表示する仕様を決めなければなりませんが、厄介な作業です。

始める前に、エクセルなどを使い表示したいフォーマットを作ってからこの作業に入ることをお勧めします。

 

負荷計算(標準)     常に定型のサイズ

負荷計算(詳細表示)  定型のサイズに詳細データの行数を加算した高さ

空調機器選定       定型のサイズにオペレーション時間データの行数を加算した高さ

総合負荷計算(詳細)  定型のサイズに詳細データの行数を加算した高さ

簡易負荷計算       常に定型のサイズ

 

画面表示は印刷と違い、ページごとの表示ではなくデフォルトサイズを超えた部分に3行ほどの空白を設定して、連続した表示としました。 

この選別はCAutoACView::OnUpdate関数で行っています。

 

個々の計算ルーチンでフラグを設定し詳細表示が必要かどうかをビュークラスに送りCAutoACView::OnUpdate関数のなかでスクリーンのスクロール範囲を計算します。

 

ドキュメントとビューの通信を理解しましょう

 

ドキュメントクラスで計算終了し結果を表示したい部分に、UpdateAllViews関数を書きこの関数が呼ばれるとビュークラスのOnUpdate関数が呼ばれます。

 

UpdateAllView関数には、三つの引数がありこれで特定の情報のやり取りが出来ます。

 

第1引数は、再画描したくないビューのポインタ(すべてのビューを更新するときには NULL を指定します)

第2引数は、Longタイプの変更に関する情報を指定します。

第3引数は、変更に関する情報が格納されているオブジェクトへのポインタ。

 

AutoACでは、第2引数に必要とするデータアレーの番号を、第3引数は、データアレーを宛てました。

従って、ドキュメントクラスで計算が終わり、表示したいときにこれらの情報がビュークラスOnUpdate関数を経由し転送されます。

ビューは、LeftViewとScrollViewの2種類ですが、印刷されるビューはScrollViewだけですが、LeftViewを書き換えたくない時は、pSender を使います。

////////////////////////////////////////////
// ドキュメントクラス

A計算ルーチン

{   詳細表示フラグ = TRUE;   

      UpDateAllView(  );} 

 

 ////////////////////////////////////////////
// CAutoAC クラスの再描画
void CAutoACView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint)
{
   CAutoACDoc* pDoc = GetDocument();
   ASSERT_VALID( pDoc );
   UINT nLines = 0;

   if( pSender != this && pHint != NULL ) {
      m_nMaxPage = 1; // 最大ページ数の初期化  この設定は取りやめました、プレビューをしないと1ページしか印刷されないため

      if( lHint >= 0 ){ // 負荷計算の時
         m_nIndex = lHint; // データファイルのインデックス番号
         if( pDoc->m_bPrintDetail == TRUE ) //負荷計算詳細の時
            nLines = GetLines_Detail( pHint, 1 );
         else // 負荷計算簡易(スクロール範囲を1ページ分)
            nLines = GetLines_Detail( pHint, 0 );
         }

      if( lHint == -1 ) nLines = GetLines_Detail( pHint, 2 ); // 総合負荷計算
      if( lHint == -2 ) nLines = GetLines_Detail( pHint, 0 ); // 空調機自動選定
      if( lHint == -3 ) nLines = GetLines_Detail( pHint, 3 ); // 簡易負荷計算

      // スクリーンのスクロール範囲を計算
      m_rectMonitor.bottom -= nLines * m_nHeight;
      CSize sizeTotal( m_rectMonitor.Width(), -m_rectMonitor.Height() );
      SetScrollSizes( MM_TWIPS, sizeTotal );
      m_nLines_Detail = nLines;
   }
   InvalidateRect( NULL, TRUE );
}


GetLines_Detailは独自に作成した関数で、詳細データがある時その表示行数を計算します。

 

////////////////////////////////////////////////////////////////////////////
// 詳細データの表示行数取得
iint CAutoACView::GetLines_Detail(CObject *pHint, int nFlag)
{
   CAirconData* pData = ( CAirconData* )pHint;
   CAutoACDoc* pDoc = GetDocument();
   ASSERT_VALID( pDoc );
   UINT nLines = 0;

   switch( nFlag ){
   case 1: // 負荷計算詳細を表示
      nLines = pData->m_ArrayWall.GetSize() + pData->m_ArrayGlass.GetSize();
      break;
   case 2: // 総合負荷計算
      nLines = pDoc->m_ArrayAircon.GetSize();
      break;
   case 3: // 簡易負荷計算
      nLines = pDoc->m_ArrayAircon.GetSize();
      break;
   default: // 1ページだけの時
      nLines = 0;
      break;
   }
return nLines; // 表示データの総行数
}

 

pHintはCObject派生クラスのCAirconDataです。表示もとのデータはCAirconDataクラスの塊であるm_ArrayAircon[nIndex]のアレーです。 ドキュメントクラスでnIndexで選定された内容を印刷する関数に渡しています。

これで、CAutoACView::OnDraw(CDC* pDC)関数をインプリメントすればモニターの表示はOKです。

 

/////////////////////////////////////////////////////////////////////////////
// CAutoACView クラスの描画
void CAutoACView::OnDraw(CDC* pDC)
{
   CAutoACDoc* pDoc = GetDocument();

   // モニターの表示のみ(印字は影響しない)
   CPoint point = m_rectMonitor.TopLeft();
  各々の表示ルーチンに飛ばす為のフラグをチェックします
   if( pDoc->m_bDyPrint == TRUE ) Draw_Main_Dy( pDC, &point );        // 総合負荷計算 の印字
   if( pDoc->m_bEqPrint == TRUE ) Draw_Main_Eq( pDC, &point );         // 空調機選定 の印字
   if( pDoc->m_bSmpPrint == TRUE ) Draw_Main_Simple( pDC, &point ); // 簡易負荷計算 の印字
   if( pDoc->m_bLdPrint == TRUE ) Draw_Main( pDC, &point );             // 負荷計算 の印字
}

 

表示ルーチンのDraw_Main( )等は、pDC->TextOut( point->x, point->y, szString ); を使い必要な座標に必要なデータを表示するコードです。

しかし、この作業は単純ですが長々と記述する必要があり、生産性は非常に悪い作業となります。

その一部であるコードを下に示します。point で表す引数で自由に移動できますので、印刷・画面表示ともに共通で使えるようにしました。

////////////////////////////////////////
// Calculation Data のモニター表示メイン
CPoint CAutoACView::Draw_Main(CDC *pDC, CPoint *point )
{
   CFont fontA;
   CPoint lp;

   fontA.CreateFont( C_POINT * 14, 0, 0, 0, FW_BOLD, FALSE, FALSE, 0,
   SHIFTJIS_CHARSET, OUT_DEFAULT_PRECIS,
   CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
   DEFAULT_PITCH | FF_SWISS, _T("MS Pゴシック"));

   CAutoACDoc* pDoc = GetDocument();
   CFont* oldfont = ( CFont* )pDC->SelectObject( &fontA );

   CString szString;
   szString.LoadString( IDS_TITLE_AUTOAC );
   pDC->TextOut(point->x, point->y - ( m_nHeight*1 ), szString );

   fontA.DeleteObject();
   pDC->SelectObject(oldfont);

   if( pDoc->m_ArrayAircon.GetSize( )) { // Aircon アレーが存在する場合
      CPoint lp1 = Draw_ProjectTitle( pDC, point, m_nHeight, m_nWidth, m_nFont );
      if( pDoc->m_Crt == TRUE && pDoc->m_bLdPrint == TRUE ) { // モニターの表示が可の場合
         if( pDoc->m_Result.GetSize( )){ // Result アレーが存在する場合
            // 最大・最小負荷集計印字
            int nIndex = m_nIndex; // アレーデータの番号
            lp = Draw_Room( pDC, &lp1, nIndex, m_nHeight, m_nWidth, m_nFont );
            lp = Draw_WallGlass( pDC, &lp, nIndex, m_nHeight, m_nWidth, m_nFont );
            lp = Draw_Other( pDC, &lp, m_nHeight, m_nWidth, m_nFont );
            lp = Draw_OtherValue( pDC, &lp, nIndex, m_nHeight, m_nWidth, m_nFont );
            lp = Draw_Fresh( pDC, &lp, nIndex, m_nHeight, m_nWidth, m_nFont );

               if( !pDC->IsPrinting() && pDoc->m_bPrintDetail == TRUE ) { // 詳細印字以外のときは、呼ばない
                  lp = Draw_Wall_Detail( pDC, &lp, nIndex );// 詳細印字(壁のデータ)
                  Draw_Glass_Detail( pDC, &lp, nIndex ); // 詳細印字(窓のデータ)
               }
            }
        }
    }

    return lp;
}

ここではコードを省略しますが、もっと生産性が高い方法がないものでしょうか。 


ページ先頭に戻る
最終更新日: 2020/02/04