none
テキストファイル印刷時の改ページ処理 RRS feed

  • 質問

  • いつもお世話になっております。arubi-momoと申します。

    Windows7 64bit  Visual Studio2010 C#で開発をしております。

    PrintDocumentを使用してテキストファイルを印刷しようとしているのですが、改ページがされずに困っています。

    調べたところ、HasMorePagesプロパティをTrueに設定すればできるようなのですが、うまく行きません。

    private void Print_Click(object sender, EventArgs e)
    {
    	//印刷の選択ダイアログを表示する
    	if (pdlg.ShowDialog() == DialogResult.OK)
    	{
    	    //PrintPageイベントハンドラの追加
    	    pd.PrintPage += new System.Drawing.Printing.PrintPageEventHandler(pd_PrintPage);
    	    //OKがクリックされた時は印刷する
    	    pd.Print();
    	}
    }
    private void pd_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
    {
        string strLine = "";
        string strPage = "";
        // ファイルオープン
        System.IO.StreamReader sr = new System.IO.StreamReader(m_PrintFilePath, Encoding.GetEncoding("Shift-JIS"));
        
        x = e.MarginBounds.Left;
        y = e.MarginBounds.Top;
        while (strLine.IndexOf(clsConstants.STR_RESULT_CONTENTS) < 0)
        {
            strPage = "";
            strPage = strPage + strLine + "\n";
            //ページの作成
            while (strLine.IndexOf(clsConstants.STR_PAGE_TERM) < 0)
            {
                strLine = sr.ReadLine();
                strPage = strPage + strLine + "\n";
            }
    	//印刷処理
            DrawPage(e.Graphics, strPage);
    	// 最終ページかどうか判断
            if (最終ページの場合)
            {
                e.HasMorePages = false;
            }
            else
            {
                e.HasMorePages = true;
            }
        }
        sr.Close();
    }
    private void DrawPage(System.Drawing.Graphics g, string strPage)
    {
        g.DrawString(strPage, m_Font, Brushes.Black, 10, 10);
    }

    これで実行すると1ページに2ページ分のデータが重なって出力されました。

    ためしに1ページ分の情報を越える行数の印刷も行ってみたのですが、やはり1ページしか出力されませんでした。

    原因お心当たりの方がいらっしゃいましたら、ご教示いただけますようお願いいたします。

    何卒、よろしくお願いいたします。

    2014年7月3日 6:47

回答

  • PrintPageイベントは、文字通り1ページ分を印刷するイベントです。複数ページを印刷する場合、このイベントを複数回発生させる必要があります。

    で、2ページ目以降用のPrintPageイベントを再度発生させるために、最終ページ以外は、そのページの描画が終わった(=PrintPageイベントを終了させる)ときに、e.HasMorePagesをtrueに設定します。

    つまり、STR_RESULT_CONTENTSでのループの内部だけをPrintPageに記述する必要があります。ループ外部は、フィールドに配置するなりすることになります。

    • 回答としてマーク arubi_momo 2014年7月4日 4:03
    2014年7月3日 6:56
  • PrintPage +=は、イベントハンドラの関連づけです。PrintDocumentオブジェクトがPrintPageイベントを発生させたときに呼び出すメソッドを登録する処理です。これは普通PrintDocumentオブジェクト1つについて1度だけで十分です。

    Printメソッドが呼び出されると、PrintDocumentオブジェクトはPrintPageイベントに+=されているメソッドを呼び出し、そのメソッドで1ページ目を描画させます。メソッドから返ってきたら、PrintDocumentオブジェクトはHasMorePagesを調べ、trueであればもう一度同じメソッドを呼び出して2ページ目を描画させます。これをHasMorePagesがfalseになるまで続けます。

    MSDNのPrintDocument.PrintPage イベントのサンプルコードがちょうどarubi_momoさんが書こうとなさっている内容にほぼ一致しているようです。(サンプルにするにはwhileの条件とかが少しサンプル向けの記述じゃありませんが)

    読み込む対象のStreamReaderがローカル変数ではなく、pd_PrintPageメソッドをもっているクラスのフィールド(メンバ変数)であるのが分かります。

    • 回答としてマーク arubi_momo 2014年7月4日 4:03
    2014年7月3日 8:22

すべての返信

  • PrintPageイベントは、文字通り1ページ分を印刷するイベントです。複数ページを印刷する場合、このイベントを複数回発生させる必要があります。

    で、2ページ目以降用のPrintPageイベントを再度発生させるために、最終ページ以外は、そのページの描画が終わった(=PrintPageイベントを終了させる)ときに、e.HasMorePagesをtrueに設定します。

    つまり、STR_RESULT_CONTENTSでのループの内部だけをPrintPageに記述する必要があります。ループ外部は、フィールドに配置するなりすることになります。

    • 回答としてマーク arubi_momo 2014年7月4日 4:03
    2014年7月3日 6:56
  • PrintDocumentは普段使わないので詳しくないのですが、overrideしたOnPrintPageメソッド内でHasMorePagesを設定しないといけないようです。

    PrintDocument で改頁する方法
    http://www1.yel.m-net.ne.jp/oss/Tips/WinApp/Tips_30003.htm

    以下にサンプルコードがあります。

    Printing In C#
    http://www.dreamincode.net/forums/topic/44330-printing-in-c%23/

    #(追記)あれ、外したかな?
    #(追記)本題とは直接関係ありませんが、usingを使って名前空間を省略した方が見やすいですね。また、
     pd.PrintPage += new System.Drawing.Printing.PrintPageEventHandler(pd_PrintPage)
    は、
     pd.PrintPage += pd_PrintPage
    と省略できます。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

    2014年7月3日 7:02
    モデレータ
  • Hongliangさま

    お世話になっております。

    早速のご回答ありがとうございました。

    PrintPageを印刷するページ分呼び出すように修正してみましたが、まだうまく行きません。今度は1ページに重なって出力されることはなくなりましたが、一番最後に呼ばれたページしか印刷されないようになってしまいました。何か間違えているのだと思うのですが。。。

    private void Print_Click(object sender, EventArgs e)
    {
        //印刷の選択ダイアログを表示する
        if (pdlg.ShowDialog() == DialogResult.OK)
        {
            m_StartPage = pdlg.PrinterSettings.FromPage;
            m_EndPage = pdlg.PrinterSettings.ToPage;
            doPrintText(pd);
            //OKがクリックされた時は印刷する
            pd.Print();
        }
    }
    private void doPrintText(System.Drawing.Printing.PrintDocument pd)
    {
        string strLine = "";
        // ファイルオープン
        System.IO.StreamReader sr = new System.IO.StreamReader(m_PrintFilePath, Encoding.GetEncoding("Shift-JIS"));
        while (strLine.IndexOf(clsConstants.STR_RESULT_CONTENTS) < 0)
        {
            m_PageData = "";
            m_PageData = m_PageData + strLine + "\n";
            //ページ区切りまで読込
            while (strLine.IndexOf(clsConstants.STR_PAGE_TERM) < 0)
            {
                strLine = sr.ReadLine();
                m_PageData = m_PageData + strLine + "\n";
            }
            //現在のページを取得
            m_CurrentPage = 現在ページ取得
            //ページ範囲内であれば書き出し
            if (m_CurrentPage >= m_StartPage && m_CurrentPage <= m_EndPage)
            {
                //PrintPageイベントハンドラの追加
                pd.PrintPage += new System.Drawing.Printing.PrintPageEventHandler(pd_PrintPage);
            }
        }
        sr.Close();
    }
    private void pd_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
    {
        DrawPage(e.Graphics, m_PageData);
        if (m_CurrentPage == m_EndPage)
        {
            e.HasMorePages = false;
        }
        else
        {
            e.HasMorePages = true;
        }
    }
    private void DrawPage(System.Drawing.Graphics g, string strPage)
    {
        g.DrawString(strPage, m_Font, Brushes.Black, 10, 10);
    }

    「pd.PrintPage += 」の「+=」は、印刷するページをプールしていて、最後にpd.Printでプールしたページを全て印刷しているのだと思っていたのですが、そこの認識が違うのでしょうか。。。都度printを呼び出す必要があるのでしょうか。

    何度も恐れ入りますが、よろしくお願いいたします。

    2014年7月3日 8:05
  • trapemiyaさま

    ご回答ありがとうございました。

    先にご教示いただいた方法で検証しており、OnPrintPageメソッドでの方法はまだ試せていません。サンプルコードまでありがとうございます。修正したコードでもう少し検証し、うまくいかなければこちらも調べてみたいと思います。

    また、usingを利用した省略の書き方のご教示ありがとうございました。

    知らなかったので勉強になります!

    2014年7月3日 8:10
  • PrintPage +=は、イベントハンドラの関連づけです。PrintDocumentオブジェクトがPrintPageイベントを発生させたときに呼び出すメソッドを登録する処理です。これは普通PrintDocumentオブジェクト1つについて1度だけで十分です。

    Printメソッドが呼び出されると、PrintDocumentオブジェクトはPrintPageイベントに+=されているメソッドを呼び出し、そのメソッドで1ページ目を描画させます。メソッドから返ってきたら、PrintDocumentオブジェクトはHasMorePagesを調べ、trueであればもう一度同じメソッドを呼び出して2ページ目を描画させます。これをHasMorePagesがfalseになるまで続けます。

    MSDNのPrintDocument.PrintPage イベントのサンプルコードがちょうどarubi_momoさんが書こうとなさっている内容にほぼ一致しているようです。(サンプルにするにはwhileの条件とかが少しサンプル向けの記述じゃありませんが)

    読み込む対象のStreamReaderがローカル変数ではなく、pd_PrintPageメソッドをもっているクラスのフィールド(メンバ変数)であるのが分かります。

    • 回答としてマーク arubi_momo 2014年7月4日 4:03
    2014年7月3日 8:22
  • いつも迅速なご回答ありがとうございます。

    根本的に考えが間違っていたようです。ループの中身のみprintPageに記載するというのはそういう意味だったのですね。

    この現場が18時で閉まってしまうため、続きは明日また試してみようと思います。結果はまたご報告いたします。

    2014年7月3日 8:50
  • Hongliangさま

    お世話になっております。

    Hongliangさまのご説明と、提示いただいたページのサンプルで、複数ページの印刷を行うことができました!

    本当に本当にありがとうございました。

    まだこれから修正するところもあるのですが、とりあえず最終的なコードを載せておきます。

    private int m_StartPage = 1;
    private int m_EndPage = 1;
    private long m_CurrentPage = 1;
    private string m_PageData = "";
    private string m_PrintFilePath = "";
    private Font m_Font;
    System.IO.StreamReader sr;
    string m_LineData = "";
    private void Print_Click(object sender, EventArgs e)
    {
        // ファイルオープン
        sr = new System.IO.StreamReader(m_PrintFilePath, Encoding.GetEncoding("Shift-JIS"));
        //PrintDocumentオブジェクトの作成
        System.Drawing.Printing.PrintDocument pd = new System.Drawing.Printing.PrintDocument();
        //PrintControllerプロパティをStandardPrintControllerに
        pd.PrintController = new System.Drawing.Printing.StandardPrintController();
        //PrintPageイベントハンドラの追加
        pd.PrintPage += new System.Drawing.Printing.PrintPageEventHandler(pd_PrintPage);
        //PrintDialogクラスの作成
        PrintDialog pdlg = new PrintDialog();
        
        //PrintDocumentを指定
        pdlg.Document = pd;
        //印刷の選択ダイアログを表示する
        if (pdlg.ShowDialog() == DialogResult.OK)
        {
            //OKがクリックされた時は印刷する
            pd.Print();
        }
        sr.Close();
    }
    private void pd_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
    {
        e.HasMorePages = true;
        m_PageData = "";
        m_PageData = m_PageData + m_LineData + "\n";
        //ページ区切りまで読込
        while (m_LineData.IndexOf(clsConstants.STR_PAGE_TERM) < 0)
        {
            m_LineData = sr.ReadLine();
            m_PageData = m_PageData + m_LineData + "\n";
        }
        m_CurrentPage = 現在ページ取得;
        //ページ範囲内であれば書き出し
        if (m_CurrentPage >= m_StartPage && m_CurrentPage <= m_EndPage)
        {
            DrawPage(e.Graphics, m_PageData);
            if (m_CurrentPage == m_EndPage)
            {
                e.HasMorePages = false;
            }
            else
            {
                e.HasMorePages = true;
            }
        }
    }
    private void DrawPage(System.Drawing.Graphics g, string strPage)
    {
        g.DrawString(strPage, m_Font, Brushes.Black, 10, 10);
    }

    今後ともよろしくお願いいたします。
    2014年7月4日 4:02