C#

CustomPreviewControl

PrintDocumentで印刷中のダイアログを表示させない方法

印刷するときなら、印刷中のダイアログを表示させないのは簡単で、ただPrintControllerを入れ替えるだけ。

// 仮に PrintDocument Document があるとして

PrintController SavedController = Document.PrintController;

try
{
    Document.PrintController = new StandardPrintController();
    Document.Print();
}
finally
{
    Document.PrintController = SavedController;
}

印刷は時間がかかるので、実際には印刷中のダイアログが出てくれたほうが都合が良い。動いていることがわかるので、ユーザーにとっても安心なはず。

問題はプレビューのほうで、PrintPreviewControlやPrintPreviewDialogを使うと、どうしても印刷中のダイアログが表示されてしまうが、うっとうしいのでやめてほしい場合がある。

例えば100ページの印刷ドキュメントがあるとして、何かのプロパティーを変えるだけで、毎回プレビューで100ページ表示されると、いちいち時間がかかって困るので、プレビューは最初の1ページだけで良いと言われることがある。そういうときの印刷中のダイアログは、これもうっとうしくてかなわないので、表示されないほうが良い。

カスタマイズしたいことはひとつだけなのに、これを実現するにはカスタムコントロールを作るしかない。だいたい、細かいところを直そうとすると、低レベルのところから作る羽目になるものだ。

印刷中のダイアログを表示させない方法は、プレビューでも基本的に同じ。簡単なサンプルを書いておく。CreatePreview()のところ。

using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Printing;
using System.Windows.Forms;

/// <summary>
/// カスタム印刷プレビュー
/// </summary>
public class CustomPreviewControl : Control
{
    /// <summary>
    /// 印刷ドキュメント
    /// </summary>
    private PrintDocument _Document;

    /// <summary>
    /// プレビューページ情報
    /// </summary>
    private PreviewPageInfo[] _Pages;

    /// <summary>
    /// ページ数
    /// </summary>
    private int _PageCount;

    /// <summary>
    /// ページ番号
    /// </summary>
    private int _PageNo;

    /// <summary>
    /// スケーリング(ピクセル単位)
    /// </summary>
    private float _Scaling;

    /// <summary>
    /// プレビューを作成するかどうかを示すフラグ
    /// </summary>
    private bool _IsCreatePreview;

    /// <summary>
    /// 印刷ドキュメントを取得または設定します。
    /// </summary>
    [Description("印刷ドキュメントを取得または設定します。")]
    public PrintDocument Document
    {
        get
        {
            return _Document;
        }
        set
        {
            _Document = value;
            InvalidatePreview();
        }
    }

    /// <summary>
    /// ページ数を取得します。
    /// </summary>
    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public int PageCount
    {
        get
        {
            return _PageCount;
        }
    }

    /// <summary>
    /// ページ番号を取得または設定します。
    /// </summary>
    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public int PageNo
    {
        get
        {
            return _PageNo;
        }
        set
        {
            if ((_PageNo != value) && (value > 0) && (value <= _PageCount))
            {
                _PageNo = value;
                Invalidate();
            }
        }
    }

    /// <summary>
    /// プレビューのスケーリングを取得します。
    /// </summary>
    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public float Scaling
    {
        get
        {
            return _Scaling;
        }
    }

    /// <summary>
    /// CustomPreviewControlクラスの新しいインスタンスを初期化します。
    /// </summary>
    public CustomPreviewControl()
    {
        SetStyle(ControlStyles.Selectable, true);
        SetStyle(ControlStyles.ResizeRedraw, true);
        SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

        Padding = new Padding(10);

        _Document = null;
        _Pages = null;
        _PageCount = 0;
        _PageNo = 0;
        _Scaling = 0.0f;
        _IsCreatePreview = false;
    }

    /// <summary>
    /// コントロールの描画時にプレビューを再作成するよう設定し、
    /// コントロールの表面全体を無効化して、コントロールを再描画します。
    /// </summary>
    public void InvalidatePreview()
    {
        _IsCreatePreview = (_Document != null);
        Invalidate();
    }

    /// <summary>
    /// プレビューを作成します。
    /// </summary>
    private void CreatePreview()
    {
        PrintController SavedController = _Document.PrintController;

        try
        {
            PreviewPrintController PreviewController = new PreviewPrintController();
            PreviewController.UseAntiAlias = true;
            _Document.PrintController = PreviewController;
            _Document.Print();
            _Pages = PreviewController.GetPreviewPageInfo();
            _PageCount = (_Pages != null) ? _Pages.Length : 0;
            _PageNo = (_PageCount > 0) ? 1 : 0;
        }
        catch
        {
            _Pages = null;
            _PageCount = 0;
            _PageNo = 0;
            throw;
        }
        finally
        {
            _Document.PrintController = SavedController;
            _IsCreatePreview = false;
        }
    }

    /// <summary>
    /// コントロールの背景を描画します。
    /// </summary>
    /// <param name="pevent"></param>
    protected override void OnPaintBackground(PaintEventArgs pevent)
    {
        // base.OnPaintBackground(pevent);

        pevent.Graphics.FillRectangle(SystemBrushes.AppWorkspace, pevent.ClipRectangle);
    }

    /// <summary>
    /// System.Windows.Forms.Control.Paint イベントを発生させます。
    /// </summary>
    /// <param name="pe"></param>
    protected override void OnPaint(PaintEventArgs pe)
    {
        // base.OnPaint(pe);

        if (_Document == null)
        {
            return;
        }

        if (_IsCreatePreview)
        {
            CreatePreview();
        }

        if ((_PageNo <= 0) || (_PageNo > _PageCount))
        {
            return;
        }

        RectangleF ClientRect = ClientRectangle;
        ClientRect.X += Padding.Left;
        ClientRect.Y += Padding.Top;
        ClientRect.Width -= Padding.Horizontal;
        ClientRect.Height -= Padding.Vertical;

        Image PageImage = _Pages[_PageNo - 1].Image;
        float WidthRate = ClientRect.Width / PageImage.Width;
        float HeightRate = ClientRect.Height / PageImage.Height;
        _Scaling = (WidthRate < HeightRate) ? WidthRate : HeightRate;

        RectangleF Rect = new RectangleF();
        Rect.Width = _Scaling * PageImage.Width;
        Rect.Height = _Scaling * PageImage.Height;
        Rect.X = ClientRect.Left + 0.5f * (ClientRect.Width - Rect.Width);
        Rect.Y = ClientRect.Top + 0.5f * (ClientRect.Height - Rect.Height);

        pe.Graphics.FillRectangle(Brushes.White, Rect);
        pe.Graphics.DrawImage(PageImage, Rect);
    }
}

印刷中のダイアログがうっとうしいだけで、本当は何ページ目を作っているかステータスバーにでも通知してくれるとありがたい。そうなっていればもっと汎用的に使えそうだ。つまり、プレビューを作るところを別スレッドにすれば、さらに良いかもしれない。

固定Scalingとか、スクロールバーやページ移動とか、実際に使うなら加えたい機能がいろいろあると思う。カスタムコントロールの開発はなかなか大変だ。

(2023/12/08 初稿)