レポートの罫線をページの下部まで表示する

f:id:accs2014:20180826144151p:plain:right:w500

 Accessのレポートはレコード数に応じて印刷範囲が下に伸びていきますので、レコードが少ない場合にはこのように下部がスカスカになります。そういうものと思えば何も問題はありませんが、どうしても最後まで罫線を表示したい場合には印刷済みの用紙を用意してそれに印刷するといった対策が必要になります。
 そこで多少実験的になりますが、このレポートを基本として若干のVBAを使い、レコードの多少にかかわらず罫線をページ下部まで出力してみます。なお、「合計」欄を含むグループフッターはページ末に表示させるものとします。


注意点

レコードソース等についての補足

 上記のレポートでは「販売ID」によって1件ごとの販売(複数の商品を含む)をグループ化し、1件の納品書として出力しています。もちろん全件一括して出力することも可能ですが、総ページ数(Pages)の表示の都合により、以下では販売IDをパラメータとし、1件=1レポートとして出力しています。

方法の選択について

 実現するにあたり、大まかに2つの方法が考えられます。一長一短があり、印刷時拡張の可・不可にも関わりますのでどちらが適切か判断が必要です。
 1つ目は「各ページに表示する行数を定め、その行数まで罫線を表示する」です。わかりやすいですが、基本的に印刷時拡張と併用できない(ページ内に収まるレコード数が変わってしまうため)ので注意が必要です。また、ヘッダーの有無等によってページごとに表示する行数を場合分けして設定する必要があります。しかし、結果をイメージしやすく、思わぬ結果になりにくいのがメリットです。
 2つ目は「ここより下には行を表示しないという高さを定め、その高さ(付近)まで罫線を表示する」です。位置(座標)を扱う手間がかかり、ちょっと設定を誤ると不自然な出力結果にもなりかねませんが、ページごとのヘッダーの有無等を気にする必要がなく、印刷時拡張にも対応できるのがメリットです。
 以下ではこの2つの実現方法をともに試してみます。

ページごとの行数を指定する方法

f:id:accs2014:20180826144148p:plain:right:w650

 さてデザインビューです。
 ちょっと複雑ですが、画像にも記されているように「販売ID」によるグループ化を2重に行います。
 1つは最初からあるもので、宛名や合計を表示させるために必要なものです。「名前」プロパティは普通に「販売IDフッター」とします(以下では「真のグループフッター」とか「合計欄」と呼びます)。
 その下位にもう1つ「販売ID」によるグループ化を加えます。これは空行(中身のない罫線)を表示させるためのものです。「名前」は「ダミーフッター」として、区別のため背景色を黄色とします。
 2つのグループフッターの高さは同じとします。


f:id:accs2014:20180826144145p:plain:right:w650

 ちなみにグループ化と並べ替えの設定はこのようになものです。上位から順に(1)販売IDによるグループ化(ヘッダー、フッターあり)、(2)販売IDによるグループ化(フッターのみあり。ダミーフッター用)、(3)商品コードによる並べ替え、となっています。


f:id:accs2014:20180826144142p:plain:right:w650

 そしてレポートモジュールに次のように入力すると設定は完了です。



Option Compare Database

'ページごとの表示行数、ページ内の行数カウンタ
Dim rows_in_page As Integer, row_count As Integer

Private Sub ダミーフッター_Format(Cancel As Integer, FormatCount As Integer)

row_count = row_count + 1

    'ページ内の行数が表示行数に達しないうち空行を表示
    '達したら表示せず真のフッターへ移る
    If row_count < rows_in_page Then
        Me.NextRecord = False
        Me.MoveLayout = True
    Else
        Me.NextRecord = True
        Me.MoveLayout = False
    End If
    
End Sub

Private Sub ページヘッダーセクション_Format(Cancel As Integer, FormatCount As Integer)

'行数カウンタリセット
row_count = 0

    'ページごとの表示行数設定。2ページ目以降はヘッダーがないため表示行数を増やす
    If Me.Page = 1 Then
        rows_in_page = 7
    Else
        rows_in_page = 12
    End If

End Sub

Private Sub 詳細_Format(Cancel As Integer, FormatCount As Integer)

row_count = row_count + 1

End Sub

 補足しますと、ページ内での詳細セクションとダミーフッターの合計表示回数が設定値未満である限り(設定値-1回になるまで)ダミーフッターの表示を繰り返し、最後に真のグループフッター(合計欄)に移ります。結果的に設定値と同じ数の行が表示されます。
 この例では1ページ目は7行を表示させ、グループヘッダーがない2ページ目以降は12行を表示させる設定としています。

f:id:accs2014:20180826144138p:plain:right:w500

 さて出力例1です。
 最初のレポートでは空白になっていた部分に、ダミーフッターによる空行(罫線)が出力され補われます。
 ページ内には(設定値どおり)合計欄も含めて7つの行が出力されているのが分かります。


f:id:accs2014:20180826144231p:plain:right:w500

 出力例2、10種の商品を含むレポートの1ページ目です。
 当然1ページ目には7行(7種の商品)しか表示されません。


f:id:accs2014:20180826144228p:plain:right:w500

 こちらが2ページ目です。
 残る3種の商品が表示されたうえで、8つの空行と1つの合計欄、計12の行(これも設定値どおり)が出力されています。


f:id:accs2014:20180826144225p:plain:right:w500

 出力例3。商品が7種ちょうどのときは合計欄だけが2ページ目に送られ、このような見た目になります。
 想定どおりの出力ではありますが、ちょっと気になるかもしれません;-o-)


最下行の基準位置を指定する方法

f:id:accs2014:20180826172228p:plain:right:w650

 デザインビューです。
 上記の方法と同じく「販売ID」によるグループ化を2重に行います。
 1つは最初からあるもので、宛名や合計を表示させるために必要なものです。「名前」プロパティは普通に「販売IDフッター」とします。
 その下位にもう1つ「販売ID」によるグループ化を加えます。これは空行(中身のない罫線)を表示させるためのものです。「名前」は「ダミーフッター」として、区別のため背景色を黄色とします。
 2つのグループフッターの高さは同じとします。
 また、この方法ではページごとの行数を一定とする必要がありませんので、詳細セクション内のテキストボックスには印刷時拡張を適用(すべてに設定してよいです)するとともに、「レイアウト」-「表形式」を適用します。これは印刷時拡張が効いた時に罫線(テキストボックス)の高さを揃えるためです。この設定と効果について詳しくは次の記事を参照願います。

www.accessdbstudy.net


f:id:accs2014:20180826144218p:plain:right:w650

 そしてレポートモジュールに次のように入力すると設定は完了です。



Option Compare Database

'ダミーフッターの表示をやめる高さ。下記より(A)-2*(B)-10で算出
'2行(2*(B))の余裕あればダミーフッターと真のグループフッターを描画でき、最終行がダミーフッターにならない
Const low_boundary As Integer = 6610
'空行が多く続いたときに無限ループとみなす回数
Const max_loop As Integer = 30

Private Sub ダミーフッター_Format(Cancel As Integer, FormatCount As Integer)

    '無限ループ判定。真なら新たな空行は表示しない。出力はされる
    If FormatCount > max_loop Then
        MsgBox ("最下行の設定位置が低い(low_boundaryが大きい)ため無限ループしています。空行の出力を中断します")
        Exit Sub
    End If

    '高さに応じ空行の表示非表示判定
    If Me.Top < low_boundary Then
        Me.NextRecord = False
        Me.MoveLayout = True
    Else
        Me.NextRecord = True
        Me.MoveLayout = False
    End If

End Sub

Private Sub ページフッターセクション_Format(Cancel As Integer, FormatCount As Integer)

'low_boundary計算用。使用後コメントに戻す
'MsgBox ("(A)ページフッター上端の高さ:" & Me.Top)

End Sub

Private Sub 販売IDフッター_Format(Cancel As Integer, FormatCount As Integer)

'low_boundary計算用。使用後コメントに戻す
'MsgBox ("(B)グループフッターの高さ:" & Me.Height)

End Sub

 冒頭のConstでダミーフッターの表示/非表示の境界線となる位置(高さ)を定めています。
 この例ではページの上端からページフッターの上端までの距離が7754twips、ダミーフッター及び真のグループフッター(合計欄)の高さがいずれも567twipsです(コード下部のMsgBoxを有効にすることで確認できます。)ので、これらより7754-567*2-10=6610twipsとしています。「-567*2」は、ページ下部の残りスペースが2行分未満ならダミーフッターの表示をやめて合計欄に移るための項です。例えば1.5行しか残っていないときにダミーヘッダーを表示してしまうと合計欄が次ページになってしまい、ダミーヘッダーがページ末に孤立するからです。「-10」はマージンです。
 なお、ダミーフッターと真のフッターの高さは同じという前提になっていますので、異なる高さにするなら別途計測し計算を変える必要があります。
 

f:id:accs2014:20180826144315p:plain:right:w500

 さて、出力例その1です。最初の例の出力例1と同じデータです。
 各セクションの設定内容は最初の例と同じですので、印刷時拡張が効かない限り基本的にどのようなデータでも最初の方法と同じ結果になります。


f:id:accs2014:20180826144312p:plain:right:w500

 出力例その2です。印刷時拡張が効いた場合の例です。最初の例のように「必ず〇行出力する」という方法と印刷時拡張を併用してしまうと合計欄が次のページに行ってしまいますが、この方法だとそうはなりません。
 結果的にページ内の行数は合計欄を含め6行となり、すべてページ内に収まっています。


f:id:accs2014:20180826144308p:plain:right:w500

 最後に出力例その3です。
 2ページにわたり印刷時拡張がはたらいています。
 1ページ目は5行で…


f:id:accs2014:20180826144303p:plain:right:w500

 2ページ目は合計欄を含め9行となっています。
 あくまで位置によってダミーフッターの出力が制御されますので、このように柔軟な出力結果が得られます。


備考

 長くなりましたが、ダミーのセクションを設けてページ内の行数ないし縦位置により表示を続けるか中断するか決める、というだけの動作ですのでグループ化設定に慣れていればあとは難しくありません。
 VBAもコメントで膨らんでしまいましたが必須部分は微々たるものです。Constの使い方とかいろいろとちぐはぐですので適当に直してください;-o-)