プログラム初心者には、以外に難しいと思われる印刷レポートのループの組み方を例にクラスの継承、オーバーライド等の使いの方を記載します。
レポートの仕様
レポートヘッダー(ReportHeader)
・印刷帳票で最初に1度印刷します。
ページヘッダー(PageHeader)
・ページ毎に最初に印刷します。(見出し等)
詳細部(Detail)
・実際の項目の内容等を印刷します。
ページフッター(PageFooter)
・ページ毎に最後に印刷します。
レポートフッター(ReportFooter)
・印刷帳票で最後に印刷します。
基本となるクラス
Public Class ReportClass
#Region "ReportControlクラス"
''' <summary>
''' レポートの管理クラス
''' </summary>
Public Class ReportControl
'ページの最大行数
Public PageMaxRows As Short
'レポートヘッダ行数
Public ReportHeaderRows As Short
'レポートフッタ行数
Public ReportFooterRows As Short
'ページヘッダ行数
Public PageHeaderRows As Short
'ページフッタ行数
Public PageFooterRows As Short
End Class
#End Region
#Region "列挙体"
''' <summary>
''' 行を取得する列挙体
''' </summary>
Public Enum DetailsRowType
Middle = 0
First
Final
End Enum
#End Region
#Region "クラス内のみで使用する変数"
'1ページの最大の行数
Private _RowMax As Integer
'現在の印刷行位置
Private _RowPoint As Integer
#End Region
'ページ番号
Private _PageNo As Integer
'行番号(最初からの位置)
Private _LineNo As Integer
'レポート印刷制御データ
Private _ReportCtrl As New ReportControl
''' <summary>
''' コンストラクタ
''' </summary>
Sub New()
_PageNo = 1
_LineNo = 1
End Sub
''' <summary>
''' ページ番号を取得する(1-)
''' </summary>
''' <returns></returns>
Public ReadOnly Property PageNo() As Integer
Get
Return _PageNo
End Get
End Property
''' <summary>
''' 先頭行からの行番号を取得する(1-)
''' </summary>
''' <returns></returns>
Public ReadOnly Property LineNo() As Integer
Get
Return _LineNo
End Get
End Property
''' <summary>
''' レポートの情報等を設定または取得します。
''' </summary>
''' <returns></returns>
Public Property ReportCtrl() As ReportControl
Get
Return _ReportCtrl
End Get
Set(ByVal value As ReportControl)
_ReportCtrl = value
End Set
End Property
''' <summary>
''' ページ行数を取得する
''' </summary>
''' <param name="mode">0:通常ページ 1:開始ページ 2:終了ページ</param>
''' <returns></returns>
Protected Function setDetailRows(Optional mode As DetailsRowType = DetailsRowType.Middle)
If mode = DetailsRowType.First Then '開始ページ
_RowMax = ReportCtrl.PageMaxRows - (ReportCtrl.PageHeaderRows + ReportCtrl.PageFooterRows + ReportCtrl.ReportHeaderRows)
_RowPoint = 0
ElseIf mode = DetailsRowType.Final Then '最終ページ
_RowMax = ReportCtrl.PageMaxRows
_RowPoint = 0
Else
_RowMax = ReportCtrl.PageMaxRows - (ReportCtrl.PageHeaderRows + ReportCtrl.PageFooterRows)
_RowPoint = 0
End If
Return True
End Function
''' <summary>
''' 改ページをする
''' </summary>
Protected Sub Pr_NextPage()
_PageNo += 1
Print_NextPage()
setDetailRows()
End Sub
''' <summary>
''' ページヘッダが必要なら出力する
''' </summary>
Protected Sub Pr_NeedPageHeader()
If _RowPoint = 0 Then
Pr_PageHeader()
End If
End Sub
''' <summary>
''' 次の行に進めページフッターが必要なら出力し改ページする
''' </summary>
Protected Sub Pr_NextLine()
_RowPoint += 1
_LineNo += 1
If _RowMax <= _RowPoint Then '改ページ
Pr_PageFooter()
Pr_NextPage()
End If
End Sub
''' <summary>
''' レポートヘッダを印刷する
''' </summary>
Protected Sub Pr_ReportHeader()
If ReportCtrl.ReportHeaderRows <= 0 Then Return
Print_ReportHeader()
_LineNo += ReportCtrl.ReportHeaderRows
End Sub
''' <summary>
''' レポートフッターを印刷する
''' </summary>
Protected Sub Pr_ReportFooter()
If ReportCtrl.ReportFooterRows <= 0 Then Return
Print_ReportFooter()
_LineNo += ReportCtrl.ReportFooterRows
Dim n As Integer = _RowMax - _RowPoint
If n < ReportCtrl.ReportFooterRows Then
Pr_NextPage()
End If
End Sub
''' <summary>
''' ページヘッダを印刷する
''' </summary>
Protected Sub Pr_PageHeader()
If ReportCtrl.PageHeaderRows <= 0 Then Return
Print_PageHeader()
_LineNo += ReportCtrl.PageHeaderRows
End Sub
''' <summary>
''' ページフッターを印刷する
''' </summary>
Protected Sub Pr_PageFooter()
If ReportCtrl.PageFooterRows <= 0 Then Return
Print_PageFooter()
_LineNo += ReportCtrl.PageFooterRows
End Sub
''' <summary>
''' 1行印刷する
''' </summary>
''' <param name="data"></param>
Protected Sub Pr_LinePrint(data As Object)
Print_LinePrint(data)
End Sub
''' <summary>
''' 詳細内容を印刷する
''' </summary>
Protected Sub Pr_Detail(datas As Object)
Print_Detail(datas)
End Sub
Protected Sub Pr_Detail(table As DataTable)
Print_Detail(table)
End Sub
''' <summary>
''' レポート印刷
''' </summary>
Public Sub Pr_Report(datas As Object)
Print_Report(datas)
End Sub
#Region "継承側で実際に出力するコードを書く関数"
Protected Overridable Sub Print_NextPage()
End Sub
Protected Overridable Sub Print_ReportHeader()
End Sub
Protected Overridable Sub Print_ReportFooter()
End Sub
Protected Overridable Sub Print_PageHeader()
End Sub
Protected Overridable Sub Print_PageFooter()
End Sub
Protected Overridable Sub Print_LinePrint(data As Object)
End Sub
Protected Overridable Sub Print_Detail(datas As Object)
Dim dataList As List(Of Object) = DirectCast(datas, List(Of Object))
If dataList.Count = 0 Then Return
For Each o As Object In dataList
Pr_NeedPageHeader()
Pr_LinePrint(o)
Pr_NextLine()
Next
If 0 < _RowPoint Then '改ページ
Pr_PageFooter()
End If
End Sub
Protected Overridable Sub Print_Detail(table As DataTable)
If table.Rows.Count = 0 Then Return
For Each row As DataRowCollection In table.Rows
Pr_NeedPageHeader()
Pr_LinePrint(row)
Pr_NextLine()
Next
If 0 < _RowPoint Then '改ページ
Pr_PageFooter()
End If
End Sub
Protected Overridable Sub Print_Report(datas As Object)
Pr_ReportHeader()
Pr_Detail(datas)
Pr_ReportFooter()
End Sub
#End Region
End Class
継承をしたクラス
Public Class MyReport
Inherits ReportClass
Dim _list As ListBox
Sub New(list As ListBox)
_list = list
Dim RCtrl As ReportControl = New ReportControl()
RCtrl.ReportHeaderRows = 1
RCtrl.ReportFooterRows = 1
RCtrl.PageHeaderRows = 1
RCtrl.PageFooterRows = 1
RCtrl.PageMaxRows = 10
ReportCtrl = RCtrl
setDetailRows(DetailsRowType.First)
End Sub
Protected Overrides Sub Finalize()
End Sub
Protected Overrides Sub Print_NextPage()
_list.Items.Add("--------------")
End Sub
Protected Overrides Sub Print_ReportHeader()
_list.Items.Add("ReportHeader")
End Sub
Protected Overrides Sub Print_ReportFooter()
_list.Items.Add("ReportFooter")
End Sub
Protected Overrides Sub Print_PageHeader()
_list.Items.Add("PageHeader")
End Sub
Protected Overrides Sub Print_PageFooter()
_list.Items.Add("PageFooter")
End Sub
Protected Overrides Sub Print_LinePrint(data As Object)
Dim s As String = DirectCast(data, String)
_list.Items.Add(s)
End Sub
End Class
呼び出し側
Private Sub Button1_Click_1(sender As Object, e As EventArgs) Handles Button1.Click
Dim dtList As List(Of Object) = New List(Of Object)
For i As Integer = 0 To 100
Dim s As String = ""
s = i.ToString()
dtList.Add(s)
Next
Dim myrep As New MyReport(ListBox1)
myrep.Pr_Report(dtList)
End Sub
上のサンプルは、リストボックスに表示するようにしています。実際は、継承を行ったクラスで印刷部分を書きます。
継承してEXCELに出力するサンプル
Imports Microsoft.Office.Interop
Public Class MyExcelReport
Inherits ReportClass
Implements IDisposable
Dim xlApp As Excel.Application
Dim xlBook As Excel.Workbook
Dim xlSheet As Excel.Worksheet
Dim olSheet As Excel.Worksheet
Dim ReportHeader_Range As Excel.Range
Dim ReportFooter_Range As Excel.Range
Dim PageHeader_Range As Excel.Range
Dim PageFooter_Range As Excel.Range
Dim Detail_Range As Excel.Range
Dim wRange As Excel.Range
Dim _DataTable As DataTable
#Region "Dispose"
Private disposedValue As Boolean = False '重複する呼び出しの制御
Protected Overridable Sub Dispose(ByVal disposing As Boolean)
If Not Me.disposedValue Then
If disposing Then
' TODO: 他の状態を解放します (マネージ オブジェクト)。
End If
ComObjectFree(wRange, True)
ComObjectFree(Detail_Range, True)
ComObjectFree(ReportHeader_Range, True)
ComObjectFree(ReportFooter_Range, True)
ComObjectFree(PageHeader_Range, True)
ComObjectFree(PageFooter_Range, True)
ComObjectFree(olSheet, True)
ComObjectFree(xlSheet, True)
ComObjectFree(xlBook, True)
ComObjectFree(xlApp, True)
End If
Me.disposedValue = True
End Sub
#Region "IDisposable Support"
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
#End Region
#End Region
Protected Overrides Sub Finalize()
MyBase.Finalize()
' Disposeが呼ばれてなかったら呼び出す
If disposedValue = False Then
Me.Dispose()
End If
End Sub
''' <summary>
''' コンストラクタ
''' </summary>
''' <param name="filtpath"></param>
Sub New(filtpath As String)
MyBase.New()
xlApp = New Excel.Application()
xlApp.Workbooks.Open(filtpath)
Dim FileName As String = System.IO.Path.GetFileName(filtpath)
xlBook = xlApp.Workbooks(FileName)
xlSheet = xlBook.Worksheets("SheetOrg")
olSheet = xlBook.Worksheets("Sheet2")
Dim RCtrl As ReportControl = New ReportControl()
ReportHeader_Range = xlSheet.Range("ReportHeader")
RCtrl.ReportHeaderRows = ReportHeader_Range.Rows.Count
ReportFooter_Range = xlSheet.Range("ReportFooter")
RCtrl.ReportFooterRows = ReportFooter_Range.Rows.Count
PageHeader_Range = xlSheet.Range("PageHeader")
RCtrl.PageHeaderRows = PageHeader_Range.Rows.Count
PageFooter_Range = xlSheet.Range("PageFooter")
RCtrl.PageFooterRows = PageFooter_Range.Rows.Count
Detail_Range = xlSheet.Range("Detail")
Dim xRange As Excel.Range = xlSheet.Range("PageRows")
RCtrl.PageMaxRows = xRange.Rows.Count
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xRange)
ReportCtrl = RCtrl
setDetailRows(DetailsRowType.First)
End Sub
''' <summary>
''' COMの解放
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="objCom"></param>
''' <param name="force"></param>
Public Shared Sub ComObjectFree(Of T As Class)(ByRef objCom As T, Optional ByVal force As Boolean = False)
If objCom Is Nothing Then
Return
End If
If Not System.Runtime.InteropServices.Marshal.IsComObject(objCom) Then
Return
End If
Try
If force Then
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(objCom)
objCom = Nothing
Else
System.Runtime.InteropServices.Marshal.ReleaseComObject(objCom)
End If
Finally
End Try
End Sub
Protected Overrides Sub Print_ReportHeader()
wRange = olSheet.Cells(LineNo, ReportHeader_Range.Column)
' SyncLock wRange
ReportHeader_Range.Copy(wRange)
System.Runtime.InteropServices.Marshal.ReleaseComObject(wRange)
' End SyncLock
End Sub
Protected Overrides Sub Print_ReportFooter()
wRange = olSheet.Cells(LineNo, ReportFooter_Range.Column)
ReportFooter_Range.Copy(wRange)
System.Runtime.InteropServices.Marshal.ReleaseComObject(wRange)
End Sub
Protected Overrides Sub Print_PageHeader()
wRange = olSheet.Cells(LineNo, PageHeader_Range.Column)
PageHeader_Range.Copy(wRange)
System.Runtime.InteropServices.Marshal.ReleaseComObject(wRange)
End Sub
Protected Overrides Sub Print_PageFooter()
wRange = olSheet.Cells(LineNo, PageFooter_Range.Column)
PageFooter_Range.Copy(wRange)
System.Runtime.InteropServices.Marshal.ReleaseComObject(wRange)
End Sub
Protected Overrides Sub Print_LinePrint(data As Object)
Dim s As String = DirectCast(data, String)
xlSheet.Range("item1").Value = s
wRange = olSheet.Cells(LineNo, Detail_Range.Column)
Detail_Range.Copy(wRange)
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(wRange)
End Sub
Protected Overrides Sub Print_Report(datas As Object)
MyBase.Print_Report(datas)
xlApp.Visible = True
System.Threading.Thread.Sleep(10000)
xlApp.Quit()
Clipboard.Clear()
End Sub
End Class
呼び出し側
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim dtList As List(Of Object) = New List(Of Object)
For i As Integer = 0 To 100
Dim s As String = ""
s = i.ToString()
dtList.Add(s)
Next
Using myrep As New MyExcelReport("C:\work\ExcelTemplate.xls")
myrep.Pr_Report(dtList)
End Using
End Sub
上記のサンプルはEXCELファイルをテンプレートとして読み込みテンプレートに従って、印刷レポートを作成します。
シートSheetOrgがテンプレートシートで出力はSheet2にレポート印刷するようになっています。

またSheetOrgは、①ReportHeader ②PageHeader ③Detail ④PageFooter ⑤ReportFooter ⑥PageRows とゆうようにセルに名前が付けられており③にお置いては、item1,item2,item3とセル毎に名前がつけられています。