2019/12/21

ワークシート上で永久カレンダーを作る




Excelのワークシートで永久カレンダーを作ります。

1.年月手動切り替えのカレンダー

まず最初は、左上セル(B2及びC2)の年月を手動で変えることで、好きな年月のカレンダーを表示できるものを作ります。
(一番下に添付したExcelファイルのSheet1)

図1

B2及びC2セルの年・月の数値をもとに、それぞれのセルには図2のような数式・数値が入っています。同じ種類の式が入っているのは赤枠で示しており、赤吹き出し内の数式は黄色セルのものです。他セルの式は相対セル位置を考慮してズラして下さい。

図2

このカレンダーは1か月用で、その初日をB2及びC2セルの数値を使ってF2セルで計算しています。その曜日をF3セル、最終日をF4セルで計算します。D5~F5で結合(マージ)してあるセルは、表示用です。
曜日の上の6行目には、その曜日に対応した数値が記入してあります。その曜日数値とF3セルの初日曜日数値を比較して、カレンダーの1週目(Excelで8行目)を作ります。
尚、このカレンダー1週目の作り方については「IF文を重ねて書く」の中で、また最終日の計算方法については「月の最終日の計算方法」の中で詳しく説明しています。

次に、カレンダーの2~4週目には、必ず数値(日付)が入ります。ですので「1つ前のセルに対して+1」する式を入れます。

5~6週目はどこかで最終日が入り、そこから先は空欄になります。数式としては「前日が空欄だったら空欄」+「前日が最終日だったら空欄」+「前日が最終日以前だったら+1」を入れます。
尚、このカレンダー5~6週目の作り方についても「IF文を重ねて書く」の中で詳しく説明しています。

2.スクロールバーで年月を切り替えるカレンダー(マクロ無し)

「手動で年月を書き換えるのはちょっと・・・」という方もいるでしょうから、右上(B5の位置)と左上(H5の位置)のボタンを操作することで、年・月を変更できるカレンダーを考えます。
(一番下に添付したExcelファイルのSheet2)


図3

シート内の数式は、上記の「年月手動切り替えカレンダー」と全く同じです。異なっているのは「年移動用のスクロールバー(B5セルの位置)」と「月移動用のスクロールバー(H5セルの位置)」のみです。
尚、スクロールバーには「フォームコントロール」と「ActiveXコントロール」の2種類がありますが、今回は「年月手動切り替えカレンダーの年月値をスクロールバーから変更する」事で効率化?を目指しているので、セル値変更の機能を持っている「フォームコントロール」で作成します。

そして、各スクロールバーの書式設定のコントロールの「リンクするセル」を、年のスクロールバーはB2セルへ、月のスクロールバーはC2セルへリンクさせます。
  
図4

尚、B2セルC2セルの年月ですが「1978年502月」というのは変ですよね。でも計算上は有り得るのです。月で12を超えた分は年に加算されていき、「1978年502月」を計算してみると「2019年10月」(=D5~F5セルの計算結果)になるのです。
このようにした理由ですが、H5セル部に置いた月を変更するスクロールバーは、最小値がゼロ以下には設定できないので、月を減らしていくとゼロでストップしてしまうからです。ストップが掛からない様にするためには、「初期値を500月くらいにしておき、西暦年をその分減らす」方法としました。(どんどん減らしたり増やしたりていけば当然どこかでストップはしますが、実用には耐えられそうです)

ですので、月を変更するスクロールバーの最大値は大きめに設定し、現在値をその中央くらいにしておいて下さい。尚、表示しているカレンダーの年月はD5~F5セルにちゃんと表されているので、B2セルC2セルは「行の非表示」や「文字色を背景と同じにする」などの手段で見えなくすると勘違いされずに済みます。

ここまでは、マクロを使用していませんので「.xlsx」で保存できます。

3.スクロールバーで年月を切り替えるカレンダー(マクロ有り)

「1978年502月 は許されないよね」という方のために少し改造しましょう。見かけも計算式もは2番目と全く同じですが、「B2セルC2セルの値 = D5~F5セルの値」になっています。
(一番下に添付したExcelファイルのSheet3)

図5

実は、このシートには以下のマクロがついています。カレンダーのワークシートにマクロを作成して下さい。
  1. Sub S2_Change()
  2.  Select Case Cells(2, 3).Value
  3.   Case 0
  4.    Cells(2, 3).Value = 12
  5.    Cells(2, 2).Value = Cells(2, 2).Value - 1
  6.   Case 13
  7.    Cells(2, 3).Value = 1
  8.    Cells(2, 2).Value = Cells(2, 2).Value + 1
  9.  End Select
  10. End Sub
図6

そして、月を変更するスクロールバーの「マクロ登録」に作成したマクロ(ここでは、S2_Change)を登録して下さい。
また、コントロールの最大値は13に、最小値を0(ゼロ)に設定して下さい。
尚、年を変更するスクロールバーへのマクロ登録は不要です。最小値を1900にしておいて下さい。(Excelの日付は1900年1月1日を起点に始まっていますので、それ以前は日付計算が出来ません)
  
図7

動きを説明します。
月の「スクロールバー」を押すたびにマクロが動きます。但しC2セル(マクロでは Cells(2,3))が0(ゼロ)か13の時以外はスルーしています。
「スクロールバー」を下げて行き、C2セルが0(ゼロ)になった時には、C2セルを12(=12月)に変更し同時に年の値を入れているB2セルの値を1つ減らしています。
「スクロールバー」を上げて行き、C2セルが13になった時には、C2セルを1(=1月)に変更し同時に年の値を入れているB2セルの値を1つ増やしています。

つまり、13月は次の年の1月、0月は前の年の12月 というExcelの年月日計算の特徴を使用したものです。

4.マクロでカレンダーを表示する

上の3つの方法はワークシート関数でカレンダーを表示していましたが、全てマクロで行うこともできます。黒塗りの部分は、今回のマクロでは使用しないデータのため、塗りつぶしてあります。
また、ワークシート上に配置したボタン「<<」「<」「>」「>>」「今月のカレンダー」は、図10のマクロの内、「minus_year」「minus_month」「plus_month」「plus_year」「kaisi」プロシージャにマクロ実行割り当てをします。
(一番下に添付したExcelファイルのSheet4)

図8

またマクロ内でデータ取得・出力する範囲を設定する為、現在表示されているカレンダーのエリアに名前(今回は、Cal_Area)を、カレンダーの年月に名前(今回は、Year_Month)を付けておきます。(各赤枠部分)
  
図9

以下のマクロをワークシートのマクロとして記載します。
  1. Sub kaisi()                    '←カレンダーがカラの時の初期化プロシージャ
  2.  Call Calendar(Year(Now()), Month(Now()))
  3. End Sub
  4. Sub Calendar(yyyy, mm)               '←カレンダーを計算・表示させるプロシージャ
  5.  Dim cal_first_day As Date             '←表示カレンダーの初日
  6.  Dim first_week,last_day As Integer         '←初日の曜日、最終日
  7.  Dim i,j,k As Integer                 '←カウンター変数
  8.  Dim cal_array(1 To 6, 1 To 7) As Variant      '←カレンダー表示部の6行7列の配列
  9.  cal_first_day = DateSerial(yyyy, mm, 1)       '←表示カレンダーの初日
  10.  first_week = Weekday(DateSerial(yyyy, mm, 1), 1)   '←初日の曜日
  11.  last_day = Day(DateSerial(yyyy, mm + 1, 1) - 1)    '←最終日
  12.  k = 1
  13.  For i = 1 To 6
  14.   For j = first_week To 7             '←1週目は初日曜日から、その他は日曜日から記入
  15.    cal_array(i, j) = k
  16.    k = k + 1
  17.    If k > last_day Then GoTo cont         '←最終日になったら2つのForを抜ける
  18.   Next j
  19.   first_week = 1                 '←2週目からは日曜日(数値1)から記入
  20.  Next i
  21.  cont:
  22.  Range("Cal_Area") = cal_array              '←カレンダーエリアに配列を書き込む
  23.  Range("Year_Month") = Year(cal_first_day) & "/" & Month(cal_first_day)    '←表示部に年/月を記入
  24. End Sub
  25. Sub plus_month()                 '←月を増加
  26.  Dim yyyy,mm As Integer
  27.  yyyy = Split(Range("Year_Month").Value, "/")(0)        '←年月エリアの年を取り出す
  28.  mm = Split(Range("Year_Month").Value, "/")(1) + 1        '←年月エリアの月を取り出し+1する
  29.  Call Calendar(yyyy, mm)              '←Calendarプロシージャの呼び出し
  30. End Sub
  31. Sub minus_month()                 '←月を減少
  32.  Dim yyyy,mm As Integer
  33.  yyyy = Split(Range("Year_Month").Value, "/")(0)
  34.  mm = Split(Range("Year_Month").Value, "/")(1) - 1
  35.  Call Calendar(yyyy, mm)
  36. End Sub
  37. Sub plus_year()                 '←年を増加
  38.  Dim yyyy,mm As Integer
  39.  yyyy = Split(Range("Year_Month").Value, "/")(0) + 1
  40.  mm = Split(Range("Year_Month").Value, "/")(1)
  41.  Call Calendar(yyyy, mm)
  42. End Sub
  43. Sub minus_year()                 '←年を減少
  44.  Dim yyyy,mm As Integer
  45.  yyyy = Split(Range("Year_Month").Value, "/")(0) - 1
  46.  mm = Split(Range("Year_Month").Value, "/")(1)
  47.  Call Calendar(yyyy, mm)
  48. End Sub
図10

図10の1行目のkaisiプロシージャは、ワークシートのカレンダーがカラの時に今月のカレンダーを表示させるものです。これは、カラの状態で年・月の変更ボタンを押されるとエラーが発生してしまうのを防止するためです。

5行目のCalendarプロシージャは、引数としてyyyy(年)とmm(月)を受け取り、カレンダーの計算をしワークシートに書き込むものです。
11~13行目は、変数「cal_first_day」「first_week」「last_day」を計算します。これは上記3種のカレンダーのF2セル,F3セル,F4セルの計算式と同じです。
その値を使い、16行目~23行目でForを二重に回して、カレンダー配列に数字を埋めていきます。まず「1」を初日曜日(first_week)に合った曜日に記入し週ごとに数字を埋めていきます。そして最終日(last_day)が来たところ(20行目)で二重のForを抜け出し、25行目で配列をかき出します。最後に年月表示部に「年/月」の表示形式で書き込みます。

一方、年及び月を増減させるボタンを押すと「plus_month」「minus_month」「plus_year」「minus_year」プロシージャが動きます。
各プロシージャの中で「Split関数」を使っています。これは文字列をある文字・記号で切り分ける関数で、例えば「2019/10/1」という文字列があった場合に「Split( "2019/10/1" , "/" )」とすると、「/」で挟まれた文字列を配列にして返します(最初と最後の部分は「/」の前・後まで)。
Split( "2019/10/1" , "/" ) ⇒ 
  0    1    2  
2019101

Split関数で返ってきたのは配列ですから、その後ろにカッコ付きで要素番号を指定すれば配列の中身が取り出せます。

例:Split( "2019/10/1" , "/" )(1) → 10

このSplit関数で年月に分割し増減させたのちCalendarプロシージャを呼び出せば、カレンダーが書き換わるわけです。


尚、カレンダーのエリア・年月エリアに名前をつけず、マクロの中で設定する方法もあります。
  1. Dim Year_Month , Cal_Area As Range
  2. Sub kaisi()
  3.  Set Year_Month = Cells(2, 4)
  4.  Set Cal_Area = Range(Cells(5, 2), Cells(10, 8))
  5.  Call Calendar(Year(Now()), Month(Now()))
  6. End Sub
図11

しかし、この方法だと、ブックを立ち上げた直後の状態では変数Year_Month と 変数Cal_Areaにデータがセットされておらず、年・月変更ボタンを押すとエラーが発生します。「開始ボタン」を押させたくないのであれば、Workbook_openイベント等でデータセットを自動で実行させる方法も考えられます。
但しカレンダーのデザインが固定しているのであれば、エリアへの名前付けの方が簡単・確実と思います。



永久カレンダー(it-010.xlsm)
セキュリティ向上を目的として「インターネット経由でダウンロードしたOfficeファイル(Excel等)のマクロは、既定でブロック」されるようにOfficeアプリケーションの既定動作が変更になりました。(2022年4月より切替開始)
解除の方法については「ダウンロードファイルのブロック解除方法」を参照下さい。