テクニカル ノート 30: 印刷と印刷プレビューのカスタマイズ
注意
次のテクニカル ノートは、最初にオンライン ドキュメントの一部とされてから更新されていません。 結果として、一部のプロシージャおよびトピックが最新でないか、不正になります。 最新の情報について、オンライン ドキュメントのキーワードで関係のあるトピックを検索することをお勧めします。
ここでは、印刷および印刷プレビューをカスタマイズする方法、CView で使用されるコールバック ルーチンの目的と、CPreviewView のコールバック ルーチンおよびメンバー関数について説明します。
問題
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 ページがまるごと表示されます。 ユーザーは、ズーム インしてその内容を詳しく調べることもできます。 機能を追加して、プレビュー モードでドキュメントを編集できるようにすることも可能です。
印刷プレビューのカスタマイズ
このメモにのみ印刷プレビューの変更の 1 つの側面でお得な情報します。プレビュー モードに UI を追加します。 他の変更方法については、ここでは扱いません。
プレビュー モードにユーザー インターフェイスを追加するには
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" などに変更します。