« 2008年6月 | トップページ | 2008年9月 »

[P18] WPF:スクロールバー(ScrollBar)を付ける

WPFでは、コントロールにスクロールバーをどうつけたらいいのか、
そのつけ方が初めはわかりません。
どうも、リストボックス(ListBox)などのコントロール(Control)にスクロールバー(ScrollBar)を付ける、という発想ではないようです。ScrollBar クラスもありますが、
簡易には、スクロールビューワー(ScrollViewer) クラスとして提供される、一種のコンテナコントロールの中に、リストボックスなどのコントロールを入れる、ということになっているようです。
すなわち、コントロールにスクロールバーをつけるのではなく、スクロールバーの仕組みの中にコントロールを入れるという、従来とは逆転した考え方をすることになるようです。

[C#]   
    ListBox listBox1 = new ListBox();
    listBox1.Height = (double)20;
        //Double.NaN のままにしておくと、スクロールバーが表示されない。
    ScrollViewer scrollViewer1 = new ScrollViewer();
    scrollViewer1.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
    scrollViewer1.Content = listBox1;
   

[XAML]   
        <ScrollViewer Name="scrollViewer1" VerticalScrollBarVisibility = "Auto">
                 <ListBox Height="20" Name="listBox1" />
        </ScrollViewer>


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

| | コメント (0)

実際の複数ページ印刷対応コード[WPF・XPS・印刷]

【前置き】

このコード(第二付属コード)は、左の欄の、

カテゴリー『パソコンの窓』内の [P14] の記事(本文・WPF: プリンター印刷(XPS))
http://hokkai53.cocolog-nifty.com/blog/2008/01/p14wpf_xps_9e5d.html


の付属資料コード(第一付属コード)=「初めての成功例コード」
http://hokkai53.cocolog-nifty.com/blog/2008/01/wpf_3199.html

の、さらにその付属コード(第二付属コード)です。


上記の本文・第一付属コード・第二付属コードの順で御覧戴くと、わかりやすい
ようになっております。(複数のウィンドウを開くと、参照しやすいです。)

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

第一付属コードは、理解には適しますが、実用には不向きです。

そこで、ここでは成功例コード(第一付属コード)を解体し、
実際に複数ページ印刷ができるような、実用型に組直しました。
PrintWork クラスという名称にしてあります。

他方、まず、同一のCanvasと描画コントールを使って、表示内容だけ変える複数ページ印刷の場合でも、一つのCanvasだけで間に合わせることはできず(エラーになる)、
同一Canvasをページ数だけ配列化しなければなりません。

また、GDI時代の、ファイル等からページごとのデータを取り込みながら、
同時にプリンターに出力していくようなコードは、WPF下では、全く不適当になったと思われます。

①データを調べ(先読み)、それを元に Canvas を作り、できあがった Canvas にデータを取り込んでしまう(本読み)過程までと、
②そのようにして完成した Canvas(の配列)を、プリンターに打ち出す過程を明確に分離するのがWPFの仕組みでは理に適っていると考えられます。

そこで、第一付属コードを実用型に組直す場合にも、そのことを考慮いたしました。

この二つのクラスを用いて、『印刷のメイン・メソッド(呼び出し元)』は、
1ページ目に Hello World A! と印刷し、2ページ目に Hello World B! と印刷するコード例となっております。

実用化のためには、PrintWork クラスの更なる改良・精緻化が必要ですが、
構造的には一応原型になっていると思います。

///////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
//----------------------------------------
//アセンブリ参照設定で以下の二つを追加する
// ReachFramework
// System.Printing
using System.Windows.Xps;
using System.Windows.Xps.Packaging;
using System.IO;
using System.Printing;
using System.Windows.Markup;//IAddChild
///////////////////////////////////////////////////////////////////
// 印刷のメイン・メソッド(呼び出し元)
///////////////////////////////////////////////////////////////////

/*
1ページ目に Hello World A! と印刷し、
2ページ目に Hello World B! と印刷する。

    ボタン(btnPrint) をクリックしたときの、イベントハンドラー(btnPrint_Click)から、
 印刷を開始するという設定です。
*/

   private void btnPrint_Click(object sender, RoutedEventArgs e)
    {
        HelloPrint helloPrint = new HelloPrint();
    helloPrint.CountData();
        helloPrint.MakeCanvas();
        helloPrint.SetData();
        helloPrint.ExePrint();
    }

///////////////////////////////////////////////////////////////////
//  Canvas の作成・データの読み込み・プリンターへの打ち出し
///////////////////////////////////////////////////////////////////

public class HelloPrint
{
    Canvas[] printCanvas;
    Label[] lblHello;
    int Pages;
   //==============================================================
    public HelloPrint()
    {
        Pages = 0;
    }
   //==============================================================
    public void CountData()
    {
        // データファイル等を『先読み』して、
    // データ量等の Canvas 作成情報を収集・確定する。
        Pages = 2;
    }
   //==============================================================
    public void MakeCanvas()
    {
        // Canvas の枠組みをページ数に応じて配列化して、
        // 完全に作り上げてしまう。
        printCanvas = new Canvas[Pages];
        lblHello = new Label[Pages];
        //-----------------------------
        for (int i = 0; i < Pages; i++)
        {
            printCanvas[i] = new Canvas(); 
            printCanvas[i].Background = Brushes.Transparent;
            //-------------- Label -----------
            lblHello[i] = new Label();
            printCanvas[i].Children.Add(lblHello[i]);
            lblHello[i].Background = Brushes.Transparent;
            lblHello[i].Width = 160;
            lblHello[i].Height = 30;
            Canvas.SetTop(lblHello[i], 100);
            Canvas.SetLeft(lblHello[i], 50);
        }
    }
   //===============================================================
    public void SetData()
    {
    // データファイル等から、表示データを『本読み込み』して、
        // Canvas 内コントロールにセットする。
        lblHello[0].Content = "Hello World A !";
        lblHello[1].Content = "Hello World B !";
    }
   //===============================================================
    public void ExePrint()
    {
    // 完成した Canvas を、FixedDocument にしてプリンターに送る。
        PrintWork printWork = new PrintWork();
        printWork.Init(Pages, 0, 0);
        for (int i = 0; i < CountPage; i++)
        {
            printWork.MakePrintingData(printCanvas[i]);
        }
        printWork.SendFixedDocumentToPrinter();
    }
}

///////////////////////////////////////////////////////////////////
// 印刷の実行コード(第1付属コードを組み替えたもの)
///////////////////////////////////////////////////////////////////

public class PrintWork

    public FixedDocument fixedDocument;
    PageContent[] pageContent;
    FixedPage[] fixedPage;
    //----------------------
    double pageWidth = 96 * 8.5;
    double pageHeight = 96 * 11;
    //----------------------
    int idxPage;
    double kitenXinPaper, kitenYinPaper;
   //================================================================   
    public PrintWork()
    {
        idxPage = -1;
        fixedDocument = new FixedDocument();       
    }
   //================================================================
    public void Init(int suPage, double kitenXinPaper, double kitenYinPaper)
    {//kitenXinPaper, kitenYinPaper =>Canvas を Paper のどの位置に描くかの問題
        pageContent = new PageContent[suPage];
        fixedPage = new FixedPage[suPage];
        //--------------------------------------------
        this.kitenXinPaper = kitenXinPaper;
        this.kitenYinPaper = kitenYinPaper;
        //------------------------------------------------
        fixedDocument.DocumentPaginator.PageSize
                                         = new Size(pageWidth, pageHeight);
    }
   //================================================================
    public void MakePrintingData(Canvas prnCanvas)
    {//各ページごとの Canvas データをここで取り込む
        this.prnCanvas = prnCanvas;
        idxPage++;
        pageContent[idxPage] = CreateFixedPageFromCanvas(idxPage);
        CreateFixedDocumentFromFixedPages(pageContent[idxPage]);
    }
//==================================================================    
    private PageContent CreateFixedPageFromCanvas(int ctrPage) 
    {//FixedPage(=>そして 最後に PageContent) にする

       pageContent[ctrPage] = new PageContent();
       fixedPage[ctrPage] = new FixedPage();

        //Canvas がFixedPage 中のどこに位置づけられるか。
        FixedPage.SetLeft(prnCanvas, kitenXinPaper);
        FixedPage.SetTop(prnCanvas, kitenYinPaper);

    //この近辺に、Canvas サイズを用紙サイズに合わせる
        //調整用のコードが加わってくるものと考えられる。

        fixedPage[ctrPage].Width = pageWidth;
        fixedPage[ctrPage].Height = pageHeight;

        fixedPage[ctrPage].Children.Add((UIElement)prnCanvas);
        //prnCanvas.Parent = null でないと、ここでエラーになります。
        //ここ以前の段階で、printCanvas=親(例えばWindow)があるとエラーになる
        //従って、同じものであっても、各ページごとに
        //別個の Canvas を用意する必要がある。

        Size sz = new Size(pageWidth, pageHeight);
        fixedPage[ctrPage].Measure(sz);
        fixedPage[ctrPage].Arrange(new Rect(new Point(), sz));
        fixedPage[ctrPage].UpdateLayout();

        ((IAddChild)pageContent[ctrPage]).AddChild(fixedPage[ctrPage]);
        return pageContent[ctrPage];
    }
   //=================================================================    
    private FixedDocument CreateFixedDocumentFromFixedPages
  (PageContent pageContent)
    {//FixedPage から FixedDocument を作る
        fixedDocument.Pages.Add(pageContent);
        return fixedDocument;
    }
  //================================================================= 
    public void SendFixedDocumentToPrinter()
    {//Printer(Queue)への書き出し
        LocalPrintServer ps = new LocalPrintServer();
        PrintQueue pq = ps.DefaultPrintQueue;

        PrintDocumentImageableArea imgArea = null;
        XpsDocumentWriter xpsdw
                         = PrintQueue.CreateXpsDocumentWriter(ref imgArea);
        //上記メソッドの引数並びは、いろいろな種類があり、
        //それによって、印刷ダイアログを表示する。
        //PrintDialog クラスがあることとの関係は、まだよくわかりません。
        xpsdw.Write(fixedDocument);
    }
}


繰り返しますが、従来のGDI時代にはある程度可能だったディスクファイルからのデータ読み出しループを、
そのまま印刷での【ページデータ読み出しループ】に使ってしまう手法は、
WPF下では通用しないようです(不可能ではないが、あまり賢いとは思えません)。

 なぜなら、① 印刷用の Canvas と表示コントロールという情報データのコンテナの枠組みを作ることと、② そこに表示する情報データは、相互に関連し合っています。

従って、ディスクファイルからのデータ読み出しをしながら、
①②の間で相互に情報のフィードバックをし、
全ページにわたる完全な Canvas の配列(①②の内容を含み、かつあらゆる点で完全に調整を済ませたもの)を作成した上で、

その完全に完成したPrintCanvasの配列を、
単純に印刷での【ページデータ読み出しループ】として、印刷処理コードの中に流し込むスタイルにするのが、WPF下での望ましいコーディングであると思われます。

//  以上

| | コメント (0)

« 2008年6月 | トップページ | 2008年9月 »