« WPF:印刷の初めての成功例コード | トップページ | [P15]WPF:ItemsSource が使用中で、ListBox.Items.Add メソッドが無効の場合 »

[P14] WPF: プリンター印刷(XPS)

[はじめに]

  WPF では、印刷の仕組みが従来のGDIのEMF形式ではなく、次世代のXPS形式標準に変更されています(但し、互換ドライバーにより、従来プリンターも動作するので、一応心配はない)。

しかし、Visual Studio 2008 の日本語版は、まだ発売されておらず、Express 版が手にはいるようになってから間もない現時点では、私のような末端にいる者がわかるような印刷のコーディングについての情報がほとんど得られません。

  そこで、私がMSDN フォーラム に質問した(下記URL)ところ、たいへん詳しい方から丁寧な解説を戴き、併せて MSDN ライブラリをたよりに格闘(?)の末、何とか Printer が動くところまでこぎ着けました。まだ、情報がない段階なので、できる限りの情報提供をしておきたいと思います。

   [注] 以上のような事情下でのことなので、後日誤りの記載であると判明する
       ものが含まれていた場合には、御容赦下さい。

[情報 1] MSDN フォーラム WPF 関連の話題の中の以下のスレッド

『 従来のPrintPageEventArgs.DrawString メソッド等を使った印刷相当のことを、
  WPF下で実現する方法』

http://social.msdn.microsoft.com/forums/ja-JP/wpfja/thread/d69419d3-e1f0-4bc3-a99d-afca7330d3de/

 

また、現時点では、やっとたどりついた私の最初の成功例コードは、なにがしかのお役に立つと思われますので、基本的には何も手を加えずに公表しておきます。
但し、自分以外の方が見てわかりやすいようにすると言う意味で手を加えてありますが、それはあくまでも、中心部分ではありません。

成功例コードは、以下のリンクから入って御覧下さい。

以下に書き続けます簡単な解説とともに御覧いただけるとわかりやすいようになって
おります。

[情報 2]

私の最初の成功例コード(C#)

成功例コード
http://hokkai53.cocolog-nifty.com/blog/2008/01/wpf_3199.html


-----------------------------------------------------------------

[2] 最初の成功例コード解説と MSDN ライブラリの関連 URL 情報

(1) WPF プログラミングにおける印刷のアウトライン

従来は、PrintPageEventArgs.DrawString メソッド等を使って、印刷のコードを書いてまいりましたが、WPF においては、唯一の絶対座標を使うことができる Canvas に、何らかの描画コントロールを配置して、その Canvas 全体が用紙1ページ分の印刷平面に相当する、という考え方をし、これが基本になります。

そして、従来の複数ページに渡る印刷過程をまともに操作しようとすると、この Canvas 情報を FixedDocument という固定文書形式に変換することになります。

  [注] そこまで求めなければ、他にも簡単な便法もあるようです。

その上で、最後に従来の感覚でいう印刷に関するコードらしき部分に入ります。
しかし、最後の部分は決まりきった短いコードで済んでしまいます。

すなわち、その前の二つの作業が今後印刷に関するコードの中心部分になってきます。

このうちの、最初にキャンバスに描く部分は WPF プログラミング一般の問題に属することになりますので、基本的にはここでは取り上げません。ただ、ここでの実験のための前提とする、最低限のシンプルなもののみを扱うだけです。

従って、ここでの中心問題は、Canvas 情報を FixedDocument に変換することであり、それを理解するのだという覚悟でとりかかる必要があります。

先にも述べましたように、本命のいき方は以上のようなことなので、ここではその本丸を攻めてマスターすることを目指します。

なお、私たちの持っているプリンターは、まだ XPS 対応ではない(デバイス・ドライバーで対応することになると思われる)のが普通ですが、最後のプリンターへの書き込み段階で『 XpsDocumentWriter クラスの Write メソッド 』というのを使いますから、プリンターが XPS 対応か否かの問題はシステムが自動的に判断して対応してくれます。したがって、そうなっているのだ、ということを承知しておくだけで足り、何の心配もいりません。コードの書き換えも不要で、どちらでも通用致します。

今、我々が取り組んでいる入り口のところが、一番険しい山場であり、ここを乗り越えれば楽になるはずですから、何から何まで新しく、わからないことだらけですが、がんばってください。

MSDN『印刷の概要』

  http://msdn2.microsoft.com/ja-jp/library/ms742418.aspx

MSDN『Windows Presentation Foundation のドキュメント』(FixedDocument 関連)

  http://msdn2.microsoft.com/ja-jp/library/ms748388.aspx

-------------------------------------------

(2) 成功例コードの具体的な説明、および、関係事項の MSDN の URL 情報

     ----------------------------

[第1段階] Canvas とその内容の用意    《コード:1》
 
本格的な複数ページ印刷が可能なコードをめざしますが、始めて未知の領域に踏み込むのですから、最低限のものにします。
すなわち、

Canvas 上に Label が1個あって、Content = "Hello World !" と表示されている。
この Hello World ! だけを印刷する1ページだけのコードを作り、その印刷が成功することが最終目標です。(但し、複数ページの場合、どこがどうなるのかは、該当箇所に記載があります。 )

ここでの最重要事項は、印刷用に用いる土台のコンテナとなる Canvas は、親要素(例えば、Window)を持っていてはならない、ということです。
 親要素を持っている Canvas で作業を始めると、ビルドをする時点でエラーになってしまい、もう一度作業を始めからやり直さなければならなくなります。

[後記注]
     コードを見るとわかるように、Canvas は、FixedPage の子要素に組み込まれること
  になります。
   そこで、複数ページ印刷の場合、各ページごとに異なる Canvas インスタンスを
  用意しなければなりません。 例えば1ページ目に "Hello World !" を打ち出し、
  同じ Canvas を利用し、Label の Content の内容のみを入れ替えて、2ページ目を
  打ち出そうとしても、エラーになってしまいます。
 
    
   従って、アプリケーションを作るプロジェクト中のXAML は、そのままにして一切いじりません。それを使うと、親要素を持たざるをえませんから、最終的に失敗します。
では、XAML を使う場合どうしたらよいかは、現在の私にはわかりません。
今後の課題として各自研究して下さい。

 [注] 私は、XAMLを使わない方針([P13]の記事参照)なので追求していませんが、
    XAMLの中に FixedPage を組み込み、その子要素として Canvas を配置する
         のかもしれません。


ここでは、言語コードのみでまいります。

     ----------------------------

[第2段階] Canvas の内容を FixedPage(=>そして 最後に PageContent) にする
                 《コード:2》

Canvas に描かれた内容が、1ページの内容になります。
そして、全体として、3ページであれば、その全体が1個の FixedDocument に
なります。
 
しかし、一気に FixedDocument を作り上げることはできません。
まず、1ページずつです。
実際には、Canvas を FixedDocument にするのではなく、Canvas(1ページ)を FixedPage の形に変換していきます。
ここでは、土台のコンテナ Canvas だけを問題にすればよく、その中にどんな描画コントロールがどれだけ含まれていようと、それに関わる必要はありません。 
そして、FixedPage への変換ができれば、最後にそれをただ ContentPage の中にひょいと入れるだけです。

関係するクラスのライブラリを開き、サンプルを眺めて繋いでいっただけのものです。
私は、MSDN ライブラリと上記 MSDN フォーラムでの稍丼さんの回答以外何も参照するものがなく(WPFの本も持っていない)、それらをつなぎあわせていったのであり、成功例コード中には丸写しで意味のわかっていないところがあります。それでも動いたのです。

コード中、一番のがんばりどころは、この[第2段階]です。

FixedDocument クラス
   こちらも見ておく必要があるでしょうが、次の[第3段階]の情報として載せてあります。

FixedPage クラス
http://msdn2.microsoft.com/ja-jp/library/system.windows.documents.fixedpage.aspx

PageContent クラス
http://msdn2.microsoft.com/ja-jp/library/system.windows.documents.pagecontent.aspx

     ---------------------------- 

[第3段階]FixedPage から FixedDocument を作る  《コード:3》

ここで、いよいよ FixedDocument を作り上げることになります。

FixedDocument は、[第2段階] で最終的に作り上げた PageContent と DocumentPaginator の二つから作り上げます。
PageContent は既にできているので、ここでは DocumentPaginator を作成します。

       
FixedDocument クラス
http://msdn2.microsoft.com/ja-jp/library/system.windows.documents.fixeddocument.fixeddocument.aspx

DocumentPaginator クラス
http://msdn2.microsoft.com/ja-jp/library/system.windows.documents.documentpaginator.aspx

     ----------------------------

[第4段階] Printer(Queue)への書き出し 《コード:4》

最終段階です。ここが、従来の感覚からすると、普通に想像される印刷コードです。

XpsDocumentWriter クラスの Write メソッドを使ってプリンター(Queue)に書き出します。
ここが、プログラミング上の、現実の印刷部分です。

なお、XpsDocumentWriter クラスの Write メソッドに関しては、私から見ますと初めはたいへんわかりにくい部分があります。
それは、他の関係クラスとの全体構造の中で、このクラスと Write メソッドが置かれている位置づけのわかりにくさに関係します。
そして、MSDNライブラリの記述自体が、はっきりいうと、そこがよくわかるように書かれておりません。
わかってしまえば、使い方にどうということはないのですが、始めての場合は私が陥ってしまったような理解(実は誤解)に陥る方もいらっしゃるのではないかと思われます。
私のように無駄な時間と労力を費やさないためにも、冒頭に示しましたMSDNフォーラムのスレッド中の、上から8番目の私のメッセージと次の9番目の稍丼さん(yayadon と同一人)のメッセージを御覧になって下さい。そこにすべてが尽くされております。

結論だけ、ここで整理しておきます(上記問題とは、初めは以下のようにすっきりわからない、ということにあります)。

XpsDocumentWriter クラスには、コンストラクタがありません。
そのオブジェクトは、
① PrintQueue.CreateXpsDocumentWriter メソッドから取得する方法と 
② XpsDocument.CreateXpsDocumentWriter メソッドから取得する方法の二通りがあります。

①のルートで取得したXpsDocumentWriter オブジェクトは、XpsDocument.xps ファイル
の書き出しをする場合に使います。
プリンターでの印刷をする場合には、②のルートで取得したXpsDocumentWriter オブジェクトを使わなくてはなりません。

なお、PrintQueue.CreateXpsDocumentWriter(); の引数にはいくつかの指定方法が
あり、それによって内容の異なる印刷ダイアログボックスが表示されるようになっております。

そして、XpsDocumentWriter.Write  メソッドに、[第3段階]で作成した FixedDocument オブジェクトを引数としてセットすれば、プリンターが動き出します。 

XpsDocumentWriter クラス
http://msdn2.microsoft.com/ja-jp/library/system.windows.xps.xpsdocumentwriter.aspx

XpsDocumentWriter.Write メソッド
http://msdn2.microsoft.com/ja-jp/library/system.windows.xps.xpsdocumentwriter.write.aspx

LocalPrintServer クラス
http://msdn2.microsoft.com/ja-jp/library/system.printing.localprintserver.aspx

PrintQueue クラス
http://msdn2.microsoft.com/ja-jp/library/system.printing.printqueue.aspx

以上です。

最初の成功例コードへのリンク
    このドキュメントの初めの部分にあります。
念のため。

では、御健闘を祈ります。


xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
目次に戻る ・・・・ 左欄のカテゴリー 【パソコンの窓】 をクリック
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

|

« WPF:印刷の初めての成功例コード | トップページ | [P15]WPF:ItemsSource が使用中で、ListBox.Items.Add メソッドが無効の場合 »

コメント

コメントを書く



(ウェブ上には掲載しません)




« WPF:印刷の初めての成功例コード | トップページ | [P15]WPF:ItemsSource が使用中で、ListBox.Items.Add メソッドが無効の場合 »