.NET 用簡易帳票フレームワーク

Version 1.0

もくじ

  1. はじめに
  2. 構成
  3. 使用方法
  4. クラスの説明
  5. 描画コマンドの説明
  6. データファイルの説明
  7. コードサンプル
  8. ダウンロード

 

はじめに

 Visual Studio の Free 版 (Express 版や Community 版) には印刷に関するコンポーネントは付属していますが、本当に基本的な機能しかもっていないので、実際に帳票 (Report) を出力するとなるとかなりのコードを書く必要があります。また、Professional 版ですが、以前は Crystal Report というフレームワークが付属していましたが、最近のバージョンではなくなってしまったので、同様に自力でコードを書く必要があります。

 そこで楽に(いくらかですが)、帳票出力ができるようなフレームワーク(クラス群)を作ってみました。下の画像は本フレームワークを使った帳票の作成例です。

 

構成

ソースファイル

クラス名継承元ソースファイル機能
DefaultValues.DefaultValsなし(Module)DefaultVals.vb既定値を保持するモジュール
LabdelDefなしLabdelDefLabel オブジェクトを構築するときに使用する。
RptBaseなし(MustInherit)RptBase基底クラス。インスタンス化不可。
RptComponentSystem.ComponentModel.ComponentRptComponent.vbPrintDocument,PrintPreviewDialog, PrintDialog, PageSetupDialog をまとめたコンポーネント
RptImageRptBaseRptImage.vb画像を描画する。
RptLabelRptTextRptLabel.vbラベルを描画する。
RptLineRptBaseRptLine.vb直線を描画する。
RptListRptBaseRptList.vb箇条書きを描画する。
RptPageRptBaseRptPage.vbページを描画する。
RptPanelRptBaseRptPanel.vbパネルを描画する。
RptReportList(Of RptPage)RptReport.vbページのコレクションを扱う。
RptShapeRptBaseRptShape.vb矩形や楕円を描画する。
RptTableRptBaseRptTable.vb表を描画する。
RptTextRptBaseRptText.vb文字列を描画する。
RptTextAreaRptBaseRptTextArea.vb複数行文字列を描画する。
TableDataDictionary(Of String, TableContent)TableData.vb表のデータを定義する。
TableData.TableContentなしTableData.vb表の内容を定義する。
TextDefなしLabelDef.vbラベル文字列の内容と属性を定義する。

名前空間

アセンブリ名名前空間
ReportClassesReportClasses

 

使用方法

プロジェクトの要件


印刷コンポーネントの機能

 本フレームワークは Visual Studio の次の標準コンポーネントを利用している。また、独自にこれらのコンポーネントを使いやすくまとめた独自コンポーネント RptComponent を用意している。

名前機能
PageSetupDialog印刷ページの設定を行うためのダイアログを開く。
PrintDialog印刷を行うときのプリンタの選択などを行うダイアログを開く。
PrintDocument印刷ドキュメント(データ)を実際に描画する。
PrintPreviewControl印刷プレビューを行うためのコントロール。PrintPreviewDialog との併用も可能だが通常はどちらか片方だけあればよい。
PrintPreviewDialog印刷プレビューを行うためのダイアログを開く。PrintPreviewControl とダイアログを一体化したもの。
RptComponentPrintDocument, PrintPreviewDialog, PrintDialog, PageSetupDialog を使いやすくまとめた独自コンポーネント。

 

以下の画像は印刷関係コモンダイアログの外観である。

PageSetupDialog
PrintPreviewDialog
PrintDialog

 

(VB) Hello World!

 もっとも簡単な例として "Hello World!" という文字列を描画する例を示す。 この例では独自コンポーネント RptComponent を使用している。プレビュー表示は PrintPreviewControl を使用する。実行例を下に示す。

(注意) RptComponent を使うとより簡単であるが複数ページを印刷できないなどの制限もある。本格的な帳票の場合は、RptComponent の使用は推奨されない。(その場合は、PrintDocument や PrintPreviewDocument コントロール等を個別にフォームに貼り付ける。)

Hello World アプリケーションの外観

プロジェクト構築手順

  1. Visual Studio で Windows Forms アプリケーションプロジェクトを作成する。
  2. プロジェクトのアセンブリ参照に ReportClasses.dll を追加する。
  3. MenuStrip コントロールをフォームに貼り付けて、Report メニューアイテムを追加する。
  4. Report メニューアイテムの下に、Print と Quit メニューを追加する。
  5. PrintPreviewControl をフォームに貼り付け、Dock プロパティを Dock.Fill に設定する。
  6. ツールボックスの全般タブに RptComponent を追加する。※
  7. RptComponent をフォームに貼り付ける。
  8. メニューアイテム Print と Quit の Name プロパティをそれぞれ "mnuPrint", "mnuQuit" に変更する。
  9. フォーム (Form1) のソースプログラムにコンストラクタを追加する。
  10. メニューアイテム Print と Quit がクリックされたときのイベントハンドラをそれぞれ追加する。

※ 全般タブを選択した状態でコンテキストメニューの「アイテムの選択」を実行する。ツールボックスアイテムの選択ダイアログが開くので、.Net Framework コンポーネントタブで「参照」ボタンをクリックして、ReportClasses.dll を選択する。これにより、RptComponent がツールボックスに表示される。


Visual Studio のエディタでソースプログラム (Form1.vb) を次のように変更する。


ソース (Form1.vb)

Imports ReportClasses
Imports System.Drawing.Printing

''' <summary>
''' Hello World!
''' </summary>
Public Class Form1
    ''' <summary>
    ''' コンストラクタ
    ''' </summary>
    Public Sub New()
        ' この呼び出しはデザイナーで必要です。
        InitializeComponent()

        ' InitializeComponent() 呼び出しの後で初期化を追加します。
        PrintPreviewControl1.Document = RptComponent1.Document
        PrintPreviewControl1.Zoom = 0.6
        RptComponent1.CallbackPrint = AddressOf PrintHello
    End Sub

    ''' <summary>
    ''' Printメニュー
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub mnuPrint_Click(sender As Object, e As EventArgs) Handles mnuPrint.Click
        RptComponent1.Document.Print()
        MessageBox.Show("既定のプリンタにドキュメントを出力しました。", "印刷完了", MessageBoxButtons.OK, MessageBoxIcon.Information)
    End Sub

    ''' <summary>
    ''' Quitメニュー
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub mnuQuit_Click(sender As Object, e As EventArgs) Handles mnuQuit.Click
        Close()
    End Sub

    ''' <summary>
    ''' 描画コールバック
    ''' </summary>
    ''' <param name="g">Graphics オブジェクト</param>
    Private Sub PrintHello(ByVal g As Graphics)
        Dim text As New RptText(g)
        text.Location = New PointF(100, 120)
        text.Text = "Hello World!"
        text.Draw()
    End Sub
End Class


ソースプログラムの説明

コンストラクタ

コンストラクタでは PrintPreviewControl に PrintDocument オブジェクトを関連付ける必要がある。これは、PrintPreviewControl1.Document = RptComponent1.Document という文で行っている。また、描画要求があったとき呼び出されるコールバックメソッドを設定する必要がある。これは、RptComponent1.CallbackPrint = AddressOf PrintHello という文で行っており、メソッド PrintHello をコールバックメソッドとして設定している。


Print メニューハンドラ mnuPrint_Click

ここでは、既定のプリンタにページを印刷するように RptComponent1.Document.Print() という文を追加してある。


コールバックメソッド PrintHello

ReportClasses.dll に含まれる RptText オブジェクトを使って "Hello World!" を印刷ページに描画している。

 

(VB) Panel と Label

 RptPanel と RptLabel クラスを使って描画するサンプルを作成する。RptLabel は枠を持つ文字列で RptText クラスから派生している。枠は表示しないようにすることもできるし、文字の色を変えたり背景色を変更することもできる。

 RptPanel は RptLabel のコンテナで、RptPanel の中では RptLabel を相対座標で指定できる。下にRptPanel と RptLabel クラスを使ったサンプルアプリケーションの外観を示す。

このサンプルでは、Hello World! サンプルのときのように RptComponent は使用していない。プロジェクト作成手順を以下に示す。

  1. Visual Studio で Windows Forms アプリケーションを作成する。
  2. 参照設定に ReportClasses.dll を追加する。
  3. フォーム (Form1) に次のコンポーネントを追加する。
  4. PrintPreviewControl と PrintDialog の Document プロパティに PrintDocument (デフォルトでは "PrintDicument1") を設定する。
  5. PrintPreviewControl の Dock プロパティを Dock.Fill に設定する。
  6. MenuStrip に Report メニュー (Name プロパティは "mnuReport") を追加する。
  7. Report メニューに Print メニュー (Name プロパティは "mnuPrint") と Quit メニュー (Name プロパティは "mnuQuit") を追加する。
  8. StatusStrip にステータスラベル (Name プロパティは "statusLabel1") を追加する。
  9. Print と Quit のメニューハンドラを追加する。
  10. PrintDocument の PrintPage イベントハンドラを追加する。

以下にこのサンプルのソースプログラムを示す。

解説

 描画処理は PrintDocument の PrintPage イベントハンドラ (PrintDocument1_PrintPage) で行っている。 ここではパネルに3つのラベルを載せて描画している。最初のラベルは、LabelDef クラスでラベルデータを定義して RptLabel.Create メソッドでラベルを作成している。2つめは RptLabel のコンストラクタ使ってラベルのインスタンスを構築している。3つ目は2つ目のラベルを複製を作成して、その属性を変更している。

 プリンタへの出力は、Print メニューハンドラ (mnuPrint_Click) で行っている。ここでは、PrintDialog を表示してプリンタを選択してから印刷するようにしている。


Imports ReportClasses

''' <summary>
''' Panel Test
''' </summary>
Public Class Form1
    ''' <summary>
    ''' コンストラクタ
    ''' </summary>
    Public Sub New()
        ' この呼び出しはデザイナーで必要です。
        InitializeComponent()

        ' InitializeComponent() 呼び出しの後で初期化を追加します。

    End Sub

    ''' <summary>
    ''' Print メニュー
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub mnuPrint_Click(sender As Object, e As EventArgs) Handles mnuPrint.Click
        If PrintDialog1.ShowDialog() = DialogResult.OK Then
            PrintDocument1.Print()
            statusLabel1.Text = "印刷完了。"
        End If
    End Sub

    ''' <summary>
    ''' Quit メニュー
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub mnuQuit_Click(sender As Object, e As EventArgs) Handles mnuQuit.Click
        Close()
    End Sub

    ''' <summary>
    ''' 描画が必要なとき
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub PrintDocument1_PrintPage(sender As Object, e As Printing.PrintPageEventArgs) Handles PrintDocument1.PrintPage
        Dim g As Graphics = e.Graphics

        ' パネルを作成
        Dim panel1 As New RptPanel(g)
        panel1.Location = New PointF(180, 150)
        panel1.Size = New SizeF(500, 200)
        panel1.BorderVisible = True
        ' パネルに含めるラベルデータを作成
        '   LabelDef クラスでラベルを定義
        Dim label1data As New LabelDef With {
            .Name = "label1",
            .Location = New PointF(1, 1),
            .AutoSize = True,
            .BorderWidth = 0,
            .Text = New TextDef With {.Font = New Font("MS 明朝", 16), .Text = "Label1 MS 明朝 16"}
        }
        panel1.Add(label1data)
        ' LabelDef クラスを使わずラベルを作成
        Dim label2 As New RptLabel(g) With {
            .Name = "label2",
            .Location = New PointF(50, 40),
            .FontSize = 12,
            .Padding = 5,
            .PaddingBottom = 4,
            .Text = "Label2 MS 明朝 12"}
        panel1.Add(label2)
        ' 属性を変更してラベルを属性
        Dim label3 As RptLabel = label2.Dup()  ' 複製を作る。
        label3.Name = "label3"
        label3.Location = New PointF(50, 90)
        label3.AutoSize = False
        label3.Size = New Size(350, 30)
        label3.Text = "Label3"
        label3.BackgroundColor = Color.WhiteSmoke
        label3.Color = Color.Blue
        label3.BorderWidth = 1  ' 境界線の太さが0だと境界線が描画されないため。
        label3.BorderBottom.BorderColor = Color.DeepPink
        label3.BorderBottom.BorderWidth = 2
        label3.BorderBottom.Size = New SizeF(350, 1)
        label3.TextAlign = TextAlign.CENTER
        panel1.Add(label3)
        panel1.Draw()
    End Sub
End Class

 

C# と RptComponent の簡単なサンプル

このサンプルは RptComponent を使った簡単なサンプルで、PrintPreviewDialog, PageSetupDialog, PrintDialog を表示して、印刷も可能である。描画内容は単純な文字列で RptText を使って描画している。


描画内容はコンストラクタ内の匿名メソッドで RptText を利用し描画している。


using System;
using System.Drawing;
using System.Windows.Forms;
using ReportClasses;

namespace RptCompoTest
{
    /// <summary>
    /// RptComponent テスト
    /// </summary>
    public partial class RptCompoForm : Form
    {
        /// <summary>
        /// コンストラクタ
        /// </summary>
        public RptCompoForm()
        {
            InitializeComponent();

            // 描画コールバックを定義する。
            rptComponent1.CallbackPrint = (g) => {
                RptText text1 = new RptText(g);
                text1.Location = new PointF(100, 80);
                text1.Text = "RptComponent のテスト";
                text1.Draw();
            };
        }

        /// <summary>
        /// Preview ボタン
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnPreview_Click(object sender, EventArgs e)
        {
            rptComponent1.ShowPreviewDialog();
        }

        /// <summary>
        /// Setup page ボタン
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSetup_Click(object sender, EventArgs e)
        {
            if (rptComponent1.ShowSetupDialog())
            {
                label1.Text = rptComponent1.Paper.PaperName;
            }
        }

        /// <summary>
        /// Print ボタン
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnPrint_Click(object sender, EventArgs e)
        {
            if (rptComponent1.ShowPrintDialog())
            {
                rptComponent1.Document.Print();
                label1.Text = "印刷しました。";
            }
        }
    }
}

 

(C#) 描画コマンドを使って描画する簡単なサンプル

このサンプルは描画コマンドを読み込んで描画を行う。ページは2枚あり、RptText と RptTextArea を使用している。

メニューは次のように定義されている。





using System;
using System.Windows.Forms;
using ReportClasses;
using ReportClasses.DefaultValues;

namespace CommandFile
{
    /// <summary>
    /// メインフォーム
    /// </summary>
    public partial class FormCommand : Form
    {
        /// <summary>
        /// 帳票オブジェクト
        /// </summary>
        private RptReport report;

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public FormCommand()
        {
            InitializeComponent();

            // 帳票オブジェクトを初期化
            report = new RptReport(printDocument1);
        }

        /// <summary>
        /// 複数行文字列データを返すコールバック
        /// </summary>
        /// <param name="name"></param>
        /// <param name="data"></param>
        private void SetTextAreaContent(string name, ref string[] data)
        {
            if (name == "TextArea1")
            {
                data = new string[] { "白い寺のような派手さはありませんが、素晴らしい作品をたくさん見られて満足であります!",
                    "夏にぴったり、すだちがさっぱり効いた生醤油うどんがおいしかったです!!",
                    "天ぷらメニューを充実させてほしいなと!",
                "店主が「初めてか?」と声をかけて初めての客には醤油のかけ方、食べ方をレクチャーしてくれる。ポイントは以下の通り。" };
            }
        }

        /// <summary>
        /// コマンドファイルを開く。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void mnuOpen_Click(object sender, EventArgs e)
        {
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                // 前のコマンドがあるかもしれないのでクリアしておく。
                report.Clear();

                // コマンドファイルを読む。
                report.ReadFile(openFileDialog1.FileName);

                // コールバック設定
                foreach (var page in report)
                {
                    page.SetTextAreaContent = SetTextAreaContent;
                }

                // プレビューを更新する。
                previewControl1.InvalidatePreview();
            }
        }

        /// <summary>
        /// 印刷する。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void mnuPrint_Click(object sender, EventArgs e)
        {
            if (printDialog1.ShowDialog() == DialogResult.OK)
            {
                // コマンドによる既定値の変更を元に戻す。
                DefaultVals.Reset();
                // ステータスバーに状態表示
                statusLabel1.Text = "印刷を準備しています。";
                Application.DoEvents();
                // RptReport の印刷開始ページをリセット
                report.PreparePrint();
                printDocument1.Print();
            }
        }

        /// <summary>
        /// アプリケーション終了
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void mnuQuit_Click(object sender, EventArgs e)
        {
            Close();
        }

        /// <summary>
        /// 描画が必要なとき
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
        {
            // 帳票オブジェクトに Graphics オブジェクトを割り当てる。
            report.GraphicsObject = e.Graphics;

            // ページがない場合は、何もせず戻る。
            if (report.Count == 0)
            {
                return;
            }

            // プレビューにページ数を設定
            previewControl1.Rows = report.Count;

            try
            {
                // 帳票を印刷(描画)
                report.Print(e);

                // ステータスバーに表示
                statusLabel1.Text = "印刷終わり。";
            }
            catch (Exception ex)
            {
                MessageBox.Show("例外を検出。" + ex.Message, "例外", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        /// <summary>
        /// Font メニュー
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void mnuFont_Click(object sender, EventArgs e)
        {
            if (fontDialog1.ShowDialog() == DialogResult.OK)
            {
                DefaultVals.FontSize = fontDialog1.Font.Size;
                DefaultVals.FontName = fontDialog1.Font.Name;
                statusLabel1.Text = "デフォルトのフォントが変更されました。";
            }
        }

        /// <summary>
        /// ページ設定
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void mnuPage_Click(object sender, EventArgs e)
        {
            pageSetupDialog1.PageSettings = printDocument1.DefaultPageSettings;

            if (pageSetupDialog1.ShowDialog() == DialogResult.OK)
            {
                printDocument1.DefaultPageSettings = pageSetupDialog1.PageSettings;
                previewControl1.InvalidatePreview();
                statusLabel1.Text = "ページ設定が変更されました。";
            }
        }
    }
}


描画コマンド

# テキスト
Text "コマンドファイルのテスト" 300 100 1
Line 100 150 750 150

# 複数行テキスト
$FontSize 12
TextArea "TextArea1" 100 250 650 200

# 改ページ
FF

# テキスト
Text "コマンドファイルのテストの2ページ目" 300 100 1

 

 

[TOP]

まいあおそふと