改ページの設定がカオス……

 VBAであまり印刷まわりの処理はしたことがなかったんですが、スケジュール管理アプリみたいなのを自作させられる中で、印刷問題にぶち当たった。

 改ページの設定がうまくいかなくて……。

 ちょこちょこっと調べてみたら、

  • FitToPagesTallプロパティ
  • FitToPagesWideプロパティ

ってのがあるので、簡単だろうな、と。

 しっかしまあ、これが泥沼……。たとえば、「HogeHogeHoge」と名付けたセル範囲をタテ8ページ×ヨコ3ページの24ページに分割しようとしたら、

With sh  '「sh」はワークシート型のオブジェクト変数'
    .PageSetup.PrintArea = "HogeHogeHoge"
    .ResetAllPageBreaks
    .PageSetup.Zoom = False
    .PageSetup.FitToPagesTall = 8
    .PageSetup.FitToPagesWide = 3
  End With

当然これで、HPageBreaksが7本にVPageBreaksが2本できると思っていたんですよ。だのに……。

f:id:akashi_keirin:20170222211657j:plain

 実行結果はコレ。

 タテ方向が4ページしかねえし(ヨコはちゃんと3ページになるが)。

 もちろん、行と列の数が常に固定されているなら、初めから改ページ位置を決めておけばいいんだが、行数が可変的なのでそれもできない。

 そこで考えたのが以下のやり方。

  • ひとまずタテ8ページ・ヨコ3ページの設定にする。
  • でも、嵌張が7つあるとは限らないので、HPageBreaksの数で条件分岐。
  • 7つ未満のときは、HPageBreaksの数までは改ページ位置の移動、7までは改ページの挿入をする。
  • 8つ以上のときは、7番目までは改ページ位置の移動、8番目以降のHPageBreaksを印刷範囲外に追い出す。

と、こんなやり方。

 場合分けさえできたらコードを書くのは簡単。

 改ページ位置の移動には、Set Worksheet.HPageBreaks(Index).Location = Range を使う。

 改ページを追加するには、Worksheet.HPageBreaks.Add Range を。

 改ページを追い出すには、Worksheet.HPageBreaks(Index).DragOff Direction, RegionIndex を使う。

 で、コードはコチラ。

Dim n As Integer
n = sh.HPageBreaks.Count    '……(1)'
If n > 7 Then
  For i = n To 8 Step -1    '……(2)'
    sh.HPageBreaks(i).DragOff Direction:=xlDown, _
                              RegionIndex:=1
  Next
ElseIf n < 7 Then
  For i = 1 To n            '……(3)'
    Set sh.HPageBreaks(i).Location = _
      sh.Range("A" & Range("Area" & i).Row + Range("Area" & i).Rows.Count)
  Next
  For i = n + 1 To 7        '……(4)'
    sh.HPageBreaks.Add _
      sh.Range("A" & Range("Area" & i).Row + Range("Area" & i).Rows.Count)
  Next
End If
For i = 7 To 1 Step -1    '……(5)'
  Set sh.HPageBreaks(i).Location = _
    sh.Range("A" & Range("Area" & i).Row + Range("Area" & i).Rows.Count)
Next

 ページを区切りたい8つの区画(セル範囲)には、それぞれ「Area1」~「Area8」と上から順に名前を定義していることにする。セル範囲に名前を定義しておくと、処理の過程で行や列が削除されて大きさが変わっても定義した名前でセル範囲を指し示せるっぽい(ちゃんと確かめたわけではない)ので便利。さらに、名前の中に数字で連番を振っておくと、Forループもできるのでさらに便利。

 (1)の

n = sh.HPageBreaks.Count

で、まずはHPageBreaksの数、つまりは嵌張の数を変数 n に代入している。

 (2)の

For i = n To 8 Step -1
  sh.HPageBreaks(i).DragOff Direction:=xlDown, _
                            RegionIndex:=1
Next

は、嵌張が8つ以上ある場合。8番目以降の嵌張を印刷範囲外に追い出す。DragOffメソッドの引数DirectionをxlDownにしているので、マウスで下方向にドラッグして追い出すのと同じ操作。このForループが終わったら、めでたく嵌張の数は7になる。

 嵌張が6つ以下の場合、まず(3)の

For i = 1 To n
  Set sh.HPageBreaks(i).Location = _
  sh.Range("A" & Range("Area" & i).Row + Range("Area" & i).Rows.Count)
Next

ですでにある嵌張の位置を区画の境目に移動する。
 区画の境目といっても、必要なのは行位置。セル範囲のRowプロパティでセル範囲の先頭行番号、Rows.Countプロパティで行数が取得できるので、その二つを足すと、次の区画の先頭行番号になるというカラクリ。
 たとえば、「1」行目から6行目までの「6」行のセル範囲の次の行は「1+6」行目でしょ?

 一見、めんどくさそうな計算ですが、割と単純な理屈ですな。

 (4)の

For i = n + 1 To 7
  sh.HPageBreaks.Add _
      sh.Range("A" & Range("Area" & i).Row + Range("Area" & i).Rows.Count)
Next

は、不足している嵌張を補う処理。すでにあった嵌張の次から7つめまで嵌張を追加していく。このForループが終わったら、無事嵌張が7つになるはず。

 で、最後に(5)の

For i = 7 To 1 Step -1
  Set sh.HPageBreaks(i).Location = _
    sh.Range("A" & Range("Area" & i).Row + Range("Area" & i).Rows.Count)
Next

で7つの嵌張の位置を設定。なぜか降順ループにしているけど、昇順ループでも大丈夫だと思う。

 とまあ、非常にめんどくさいことをしている。たぶん、Excelの改ページの理屈が分かっていたらもっとスマートなコードになると思う。

 実行すると、

f:id:akashi_keirin:20170222220643j:plain

ほれ、この通り、ちゃんとタテ8ページで、区切り位置もバッチリ。

 印刷まわりの処理は、イマイチよく分かっとらん部分が多いので、何かご意見あったらください。