■VB.Netによる開発その4(印刷)

 PostgreSQLのデータを読み込んで以下のような帳票(A4横)を印刷をしてみます。
 
 印刷は自作のクラスを使います。
 ここで公開しているソースをcls_Print_としてプログラムに組み込みます。

 ●Form1変数
    '### 印刷用構造体 ####################################################################
    Private Structure P_Rec_
        Public P_Kubun As Integer    '区分
        Public P_date As Date        '日付
        Public P_code As String      '商品コード
        Public P_name As String      '商品名
        Public P_quantity As Decimal '数量
        Public P_price As Decimal    '単価
        Public P_money As Decimal    '金額
    End Structure
    '### 印刷データ保持用ジェネリックコレクション ###################################################
    Private p_List As System.Collections.Generic.List(Of P_Rec_)
    '### 印刷開始フラグ ##############
    Private Fl_Print_First As Boolean

 ○PrintDocumentに直接データを渡せないのでForm変数を作成して参照する事とします。
  DBのデータを読み込んでジェネリックコレクションに追加、ジェネリックコネクションから
  データを取り出して印刷する事とします。

 ●印刷開始
       'DBデータから印刷
        Dim pPaperSz As System.Drawing.Printing.PaperKind
        Dim pkSize As System.Drawing.Printing.PaperSize

        '### データベースから印刷用データを配列へセット ###############################
        If Print_data_make() = False Then
            Exit Sub
        End If

        '### プリンター名を直接指定して印刷 ############################################
        PrintDocument3.PrinterSettings.PrinterName = "PrimoPDF"

        '### 印刷用紙サイズ設定 #######################################################
        pPaperSz = Printing.PaperKind.A4                 'A4 サイズの定数
        'サポートされている用紙サイズの一覧を取得
        For Each pkSize In PrintDocument3.PrinterSettings.PaperSizes
            '指定の用紙サイズがサポートされているか
            If pkSize.Kind = pPaperSz Then
                '指定の用紙サイズが見つかったら用紙サイズを設定する
                PrintDocument3.DefaultPageSettings.PaperSize = pkSize
            End If
        Next

        '### 用紙方向横向き/縦向きで印刷 ################################################
        PrintDocument3.DefaultPageSettings.Landscape = True      '横向き

        '### 印刷ドキュメント名設定 ######################################################
        PrintDocument3.DocumentName = DateTime.Now.ToString("yyyyMMdd_HHmmss") & "_TestPrint" '印刷ドキュメント名設定

        '### 印刷開始フラグ ##############################################################
        Fl_Print_First = True

        '### 印刷 ########################################################################
        PrintDocument3.Print()

  ○普通の印刷とほぼ同じです。Print_data_make() がデータベースを読み込んで
   ジェネリックコネクションへ追加するルーチンです。

 ●データベースを読み込んでジェネリックコネクションへ追加
    Private Function Print_data_make() As Boolean
        '印刷データを作成する
        Dim cn As New NpgsqlConnection 'コネクション
        Dim dr As NpgsqlDataReader     'DataReadr
        Dim cmd As New NpgsqlCommand   'SQL発行用
        Dim W_Sql As String            'SQL作成用
        Dim W_Rec As New P_Rec_        '構造体(明細用)
        Dim Fl_Open As Boolean = False 'DBオープンフラグ
        Dim W_Kei As Decimal = 0       '金額合計ワーク
        '### 接続設定 ###
        cn.ConnectionString = "Server=[サーバのIPアドレス];Port=[接続に使用するポート];User Id=[ユーザー];Password=[パスワード];Database=[接続するDB名];Preload Reader = true;"

        '### 処理開始 ###
        Try
            '--- 接続 ---
            cn.Open()
            Fl_Open = True

            W_Sql = "SELECT sal.date AS date , sal.code AS code , item.name AS name " & _
                    ", sal.quantity AS quantity , sal.price AS price , sal.money AS money " & _
                    "FROM ms_sales sal JOIN cm_item item on sal.code = item.code " & _
                    "ORDER BY sal.date , sal.code , sal.id "

            '--- DataReaderを使用したデータの読込 ---
            cmd = New NpgsqlCommand(W_Sql, cn)
            dr = cmd.ExecuteReader
            p_List = New System.Collections.Generic.List(Of P_Rec_) '印刷データ配列初期化
            '--- データを読み込んで配列に追加 ---
            While dr.Read
                W_Rec = New P_Rec_
                W_Rec.P_Kubun = 0
                W_Rec.P_date = dr("date")
                W_Rec.P_code = dr("code")
                W_Rec.P_name = dr("name")
                W_Rec.P_quantity = dr("quantity")
                W_Rec.P_price = dr("price")
                W_Rec.P_money = dr("money")

                W_Kei = W_Kei + dr("money")
                p_List.Add(W_Rec)
            End While

            '--- 最終レコード(合計)追加 ---
            W_Rec.P_Kubun = 1
            W_Rec.P_date = DateTime.Now
            W_Rec.P_code = 0
            W_Rec.P_name = ""
            W_Rec.P_quantity = 0
            W_Rec.P_price = 0
            W_Rec.P_money = W_Kei

            p_List.Add(W_Rec)

            dr.Close()

            Return True
        Catch ex As Exception

            MessageBox.Show("エラーが発生しました" & ex.Message)
            Return True

        Finally
            '--- 切断 ---
            If Fl_Open = True Then
                cn.Close()
            End If
        End Try
    End Function

 ○データを取り出してジェネリックコネクションへ追加します。
  VB.Netは印刷部が変わったので合計等はこちらでセットするのが良いのかなと思います。

 ●印刷実装部
    Private Sub PrintDocument3_PrintPage(sender As System.Object, e As System.Drawing.Printing.PrintPageEventArgs) Handles PrintDocument3.PrintPage
        '--- 印刷・配列から -----------------------------
        Static W_Page As Long         'ページ数
        Static W_Gyo As Long          '行数
        Static Data_Count As Long     '配列のカウンタ
        Dim clsPrt As New cls_print_  '印刷クラス

        'プリンタの初期設定を行う
        clsPrt.Printer_Setting(e, Me.PrintDocument1, New Point(140, 50))

        '印刷初回設定
        If Fl_Print_First = True Then
            W_Page = 0
            Data_Count = 0
            Fl_Print_First = False
        End If

        'ヘッダー印刷
        W_Page = W_Page + 1

        clsPrt.Print_Moji(e, New PointF(20, 1), "*** 売上一覧 ***")
        clsPrt.Print_Moji(e, New PointF(57, 1), "Date:" & DateTime.Now.ToString("yyyy/MM/dd HH:mm"))
        clsPrt.Print_Moji(e, New PointF(80, 1), "Page:" & W_Page.ToString("###"))

        clsPrt.Print_Moji(e, New PointF(8, 4), "日 付")
        clsPrt.Print_Moji(e, New PointF(18, 4), "商品名")
        clsPrt.Print_Moji(e, New PointF(56, 4), "数量")
        clsPrt.Print_Moji(e, New PointF(69, 4), "単価")
        clsPrt.Print_Moji(e, New PointF(84, 4), "金額")

        '罫線印刷
        '横
        clsPrt.Draw_Line(e, New PointF(5, 3), New PointF(89, 3))
        For i10 As Integer = 6 To 26 Step 2
            '太さ・破線の間隔を省略
            clsPrt.Draw_Line(e, New PointF(5, i10), New PointF(89, i10))
        Next
        '縦
        clsPrt.Draw_Line(e, New PointF(5, 3), New PointF(5, 26))
        clsPrt.Draw_Line(e, New PointF(17, 3), New PointF(17, 26))
        clsPrt.Draw_Line(e, New PointF(49, 3), New PointF(49, 26))
        clsPrt.Draw_Line(e, New PointF(61, 3), New PointF(61, 26))
        clsPrt.Draw_Line(e, New PointF(74, 3), New PointF(74, 26))
        clsPrt.Draw_Line(e, New PointF(89, 3), New PointF(89, 26))

        '明細印刷
        '初期化
        W_Gyo = 0

        Do
            If p_List(Data_Count).P_Kubun = 0 Then
                clsPrt.Print_Moji(e, New PointF(6, 6 + W_Gyo * 2), p_List(Data_Count).P_date.ToString("yyyy/MM/dd"))
                clsPrt.Print_Moji(e, New PointF(18, 6 + W_Gyo * 2), p_List(Data_Count).P_name)
                clsPrt.Print_Moji(e, New PointF(49, 6 + W_Gyo * 2), p_List(Data_Count).P_quantity.ToString("#,###"), 11, "R")
                clsPrt.Print_Moji(e, New PointF(61, 6 + W_Gyo * 2), p_List(Data_Count).P_price.ToString("#,###"), 12, "R")
                clsPrt.Print_Moji(e, New PointF(74, 6 + W_Gyo * 2), p_List(Data_Count).P_money.ToString("#,###"), 14, "R")
            Else
                clsPrt.Print_Moji(e, New PointF(18, 6 + W_Gyo * 2), "●● 合 計 ●●")
                clsPrt.Print_Moji(e, New PointF(74, 6 + W_Gyo * 2), p_List(Data_Count).P_money.ToString("#,###"), 14, "R")
            End If

            W_Gyo = W_Gyo + 1
            Data_Count = Data_Count + 1

            If Data_Count >= p_List.Count Then
                e.HasMorePages = False
                Exit Sub
            End If
            If W_Gyo > 9 Then
                e.HasMorePages = True
                Exit Sub
            End If
        Loop

    End Sub

 ○印刷実装部です。
  ・Fl_Print_First による初回判定
   変数宣言と同時に0クリアすると1回目の印刷は良い物の、2回目の印刷で初期化されません。
   その為Form変数でフラグを持ち、他の初期化処理を含めて行っています。
  ・Data_Count
   配列の何行目かを管理する変数です。上記フラグで印刷開始時にクリアされます。

  ※あとはさほど特別な処理は無いと思います。
   改ページでルーチンを一旦抜ける事を考えると事前のデータである程度
   出力する形にデータを整えておいて印刷側は簡単にすると良いと思います。

   印刷はクラスで簡単になっているかな?市販の印刷用ソフトなんかを使ってみたいですね。

   まだ思考錯誤中なので意見・アドバイス等あればブログまでお願いします。

トップへ戻る