テクニカル ノート 30: 印刷と印刷プレビューのカスタマイズ
更新 : 2007 年 11 月
メモ : |
---|
次のテクニカル ノートは、最初にオンライン ドキュメントの一部とされてから更新されていません。結果として、一部のプロシージャおよびトピックが最新でないか、不正になります。最新の情報について、オンライン ドキュメントのキーワードで関係のあるトピックを検索することをお勧めします。 |
ここでは MFC (Microsoft Foundation Class) のコントロール バー クラスである CControlBar、CStatusBar、CToolBar、CDialogBar、CDockBar について説明します。
問題
MFC (Microsoft Foundation Class) の機能によって、ほとんどの印刷や印刷プレビューを実現できます。多くの場合、わずかなコードを追加するだけで、ビューを印刷したりプレビューできるようになります。印刷を最適化することもできますが、それには多くの労力を必要とし、場合によっては印刷プレビュー モードにユーザー インターフェイスを追加する必要もあります。
効率的な印刷
MFC アプリケーションの標準的な印刷方法では、すべてのグラフィカル デバイス インターフェイス (GDI: Graphical Device Interface) 出力呼び出しは、Windows によって、メモリ上のメタファイルに対して行われます。EndPage が呼び出されると、Windows はプリンタが 1 ページの印刷に必要とする物理的なバンドのそれぞれに対して 1 回ずつメタファイルを出力します。このレンダリング中、GDI は周期的にアボート プロシージャに対して印刷を継続するかどうかを問い合わせます。一般にアボート プロシージャでは、ユーザーが印刷ダイアログを使って印刷ジョブを中止できるように、メッセージを処理します。
残念ながら、この処理によって印刷プロセスが遅くなっています。標準的な方法よりも高速に印刷を行いたい場合は、手作業によるバンド処理を実装する必要があります。
印刷バンド処理
手作業でバンド処理を行うには、1 ページについて複数回 (バンドあたり 1 回) OnPrint 関数が呼び出されるように、印刷ループを実装し直します。印刷ループは viewprnt.cpp ファイルの OnFilePrint 関数で実装されています。CView の派生クラスでこの関数をオーバーライドして、print コマンドを処理するメッセージ マップのエントリから、独自の印刷関数が呼び出されるようにします。OnFilePrint ルーチンをコピーして、バンド処理を行うように印刷ループを変更してください。また、独自の印刷関数にバンド処理用の四角形を渡し、印刷されるページの範囲を基準にして最適な描画を行うことができます。
また、バンドを描画している間は、QueryAbort 関数を定期的に呼び出す必要があります。この関数を呼び出さないと、アボート プロシージャが呼び出されないので、ユーザーは印刷ジョブをキャンセルできません。
印刷プレビュー : ユーザー インターフェイスを備えた電子用紙
印刷プレビューの本質は、画面をプリンタに変身させることにあります。既定では、メイン ウィンドウのクライアント領域に、1 ページか 2 ページがまるごと表示されます。ユーザーは、ズーム インしてその内容を詳しく調べることもできます。機能を追加して、プレビュー モードでドキュメントを編集できるようにすることも可能です。
印刷プレビューのカスタマイズ
ここでは、印刷プレビューの変更について、そのユーザー インターフェイスの追加という観点からのみ説明します。他の変更方法については、ここでは扱いません。
プレビュー モードにユーザー インターフェイスを追加するには
CPreviewView からクラスを派生します。
必要なユーザー インターフェイスに対して、コマンド ハンドラを追加します。
画面の表示に対して変更を加えるには、OnDraw 関数をオーバーライドして CPreviewView::OnDraw 関数を呼び出した後に独自の描画を行います。
OnFilePrintPreview
これは、印刷プレビュー用コマンド ハンドラです。既定の実装を次に示します。
void CView::OnFilePrintPreview()
{
// In derived classes, implement special window handling here
// Be sure to Unhook Frame Window close if hooked.
// must not create this on the frame. Must outlive this function
CPrintPreviewState* pState = new CPrintPreviewState;
if (!DoPrintPreview(AFX_IDD_PREVIEW_TOOLBAR, this,
RUNTIME_CLASS(CPreviewView), pState))
{
// In derived classes, reverse special window handling
// here for Preview failure case
TRACE0("Error: DoPrintPreview failed");
AfxMessageBox(AFX_IDP_COMMAND_FAILURE);
delete pState; // preview failed to initialize,
// delete State now
}
}
DoPrintPreview 関数は、アプリケーションのメイン ペインを非表示にします。ステータス バーのようなコントロール バーは、pState->dwStates メンバでそれらを設定することによって保持できます。このメンバはビット マスクで、個別のコントロール バーに対応したビットは AFX_CONTROLBAR_MASK( AFX_IDW_MYBAR) で定義されています。ウィンドウ pState->nIDMainPane は、自動的に非表示、再表示されるウィンドウです。DoPrintPreview は、標準のプレビュー UI のボタン バーを作成します。他のウィンドウを表示したり非表示にするなど、特別なウィンドウ処理が必要な場合は、DoPrintPreview を呼び出す前にその処理を実行してください。
既定では、印刷プレビューが終了すると、コントロール バーは元の状態に戻され、メイン ウィンドウが再表示されます。そのときに特別な処理が必要な場合は、EndPrintPreview 関数をオーバーライドしてください。DoPrintPreview 関数が失敗した場合にも、この関数で特別な処理を行います。
DoPrintPreview は次の値をパラメータとして呼び出されます。
プレビュー ツール バーのダイアログ テンプレートのリソース ID。
印刷プレビューで印刷が実行されるビューへのポインタ。
プレビューのビュー クラスのランタイム クラス。これは DoPrintPreview で動的に作成されます。
CPrintPreviewState ポインタ。CPrintPreviewState 構造体 (他にも状態を保存する場合は、その派生構造体) は、フレーム上に作成しないでください。DoPrintPreview 関数はモードレスなので、この構造体は EndPrintPreview 関数が呼び出されるまで残っている必要があります。
メモ : 印刷で分割されたビューやビュー クラスのサポートが必要な場合には、2 番目のパラメータとしてそのオブジェクトへのポインタを渡します。
EndPrintPreview
印刷プレビュー モードを終了するために呼び出されます。印刷プレビューで最後に表示したドキュメントのページに移動するときは、EndPrintPreview 関数を使います。pInfo->m_nCurPage メンバは、直前に表示されたページ (2 ページ表示の場合は左側のページ) を指すので、カーソルの位置と合わせて、ユーザーがページのどの位置にいるかがわかります。アプリケーションのビューの構造はフレームワークにはわからないので、選択された位置に移動するためのコードを作成する必要があります。
印刷プレビューに関するほとんどの操作は、CView::EndPrintPreview 関数の呼び出しの前に行う必要があります。この関数を呼び出すと、DoPrintPreview 関数の結果は失われ、pView、pDC、pInfo も削除されます。
// Any further cleanup should be done here.
CView::EndPrintPreview(pDC, pInfo, point, pView);
CWinApp::OnFilePrintSetup
この関数は、[プリンタの設定] メニュー項目に割り当てられています。多くの場合、この関数をオーバーライドする必要はありません。
ページの名前付け規約
ページの番号と順序も問題となります。簡単なワード プロセッサ アプリケーションの場合は、これは難しい問題ではありません。大部分の印刷プレビュー システムでは、印刷された各ページがドキュメントの 1 ページに対応すると見なされているためです。
しかし、汎用的に活用できるようにする場合、いくつか考慮しなければならない点があります。たとえば、CAD システムを想定してください。ユーザーが何枚かの E サイズの用紙にまたがる図面を持っているとします。E サイズ (またはより小さな縮小印刷) のプロッタでは、ページ付けは簡単です。しかし、レーザー プリンタでは、1 シートにつき、A サイズのページが 16 ページ印刷されます。この場合、印刷プレビューは何を "ページ" として考えればよいのでしょうか。
最初に述べたように、印刷プレビューはプリンタのように動作します。そのため、ユーザーは選択したプリンタによる出力内容を参照できます。各ページに印刷するイメージは、このビューによって決定されます。
"Page 1" または "Pages 1-2" のように、ページごとに 1 つの番号を示すことができる場合は、CPrintInfo 構造体のページ記述文字列を使って、そのページ番号を表示できます。この文字列は CPreviewView::OnDisplayPageNumber 関数の既定の実装で使われています。表示を変更するには、この仮想関数をオーバーライドし、文字列を "Sheet1, Sections A, B" などに変更します。