ファビコン。井の家紋。自作のカレンダーフォームを作ろう | エクセルマクロ(VBA)実践蔵(じっせんぐら)

前の項目 - 別ファイルへシートコピー後、シート名変更をして保存する
次の項目 - 自作カレンダーフォームをショートカットキーで呼び出そう

自作のカレンダーフォームを作ろう最終更新日:2024-05-13

カレンダーから日付をセルへ入力できるようにカレンダーフォームを作成していきます。

2023/6/11追加対応として祝日表示に対応しました。祝日表示の追加対応

このページでは、カレンダフォームを1から作成する手順を記載していくのですが、その前に、カレンダーフォームの呼び出し方を4種類用意しているので、そのパターンを説明したいと思います。

[1][2]は、標準エクセルファイル(マクロ無効のファイル)上で動作させられます。[3]は、マクロ有効ブック上で動作させられます。[4]は作業するソフトウェア(アプリ)の裏に隠れますが、クリップボードのためどこにでも貼り付けられます。

このページの末尾にあるエクセルファイルのダウンロードでは、[1]のものをダウンロード可能です。

[2]については、自作カレンダーフォームをショートカットキーで呼び出そうに記載しているので、ダウンロードはそちらからになります。

[3]については、日付セルクリック時に自作カレンダーフォームを呼び出そうに記載しているので、そちらからダウンロード可能です。

[4]については、ウィジェット風カレンダーフォームの表示に記載しているので、そちらからダウンロード可能です。

補足:自作のユーザフォームは呼び出したエクセルに紐づけられます。カレンダーフォームを含むマクロ有効ブックにボタンをつけて起動してしまうと、通常エクセルファイルを開き、カレンダーを操作したくても、後ろに隠れてしまいます。そのために、通常エクセルファイルや作業したいエクセルファイルからマクロを呼び出す必要があります。

その方法が、このページに記載している「マクロの実行からの呼び出し」と次の記事の「ショートカットキーによる呼び出し」になります。「日付セルクリック時に呼び出す」ものは、カレンダーフォームを含むマクロ有効ブック上での操作になります。(23/6/18 追加対応として、マクロ有効ブックを保存するときに同じファイル名で標準エクセルファイルを保存するマクロを追加しました。作業用と配布用の内容が同じになるように配慮しました。)「スクリプトからの呼び出し」によるカレンダーは、デスクトップにはりついているイメージで、作業アプリケーションの後ろに隠れるものです。Microsoftの発表より、将来的には、スクリプトは使えなくなるようです。

カレンダーフォームの作成

表示できるカレンダーフォームの見た目

VBAカレンダーフォーム

Visual Basic Editorを開き、プロジェクトエクスプローラで右クリックして、「挿入」->「ユーザフォーム」を選択して、UserFormオブジェクトを作成します。プロパティウインドウ内のオブジェクトの名前は、CalenderFormに変更します。プロパティウインドウが表示されていない場合は、エディタメニューの「表示」->「プロパティウインドウ」の手順で表示できます。

カレンダーフォーム

一番上に表示中の年月を表示させるラベルLabelオブジェクト MonthLabelを作成し配置します。図内ではCaptionが「2019年12月」としていますが、見た目の確認の為に記入しています。フォントの色は、ForeColorより変更できます。Fontの右端「…」をクリックすると、フォント名、スタイル、サイズが変更できるため、フォント名「MS UI Gothic」、スタイルを「太字」、サイズ「12」を選んでいます。この辺はお好みで変えてください。

年月ラベルフォントカラーの変更ラベルフォントの変更

その左に、月表示を前月変更するボタン(CommandButtonオブジェクト)を配置し、オブジェクト名に「PreMonth」、Captionに「<<」を設定する。同様に、右側に次の月を表示させるボタン(CommandButtonオブジェクト)を配置し、オブジェクト名に「NextMonth」、Captionに「>>」を設定する。

「今月に戻る」を表示するラベルとして、Labelオブジェクト MonthNowを配置。表示年月が今月と一致している場合、このラベルは非表示とし、表示年月が今月でない場合にのみ表示するように制御させていくことにする。

曜日ラベル

曜日を表示するラベルを順番にLabelオブジェクト「SUN」「MON」「TUE」「WED」「THR」「FRI」「SAT」として、初期値としてCaptionに「日」「月」「火」・・・と設定していきます。また、日曜と土曜の色を変えるために、ForeColorを変更していきます。曜日のラベルについては、コードでは一度もアクセスしていません。初期値のまま変更しないため、オブジェクト名については初期値のままでも問題ありません。

その下に日付を設定するラベルとしてLabelオブジェクト「DayLabel1」「DayLabel2」「DayLabel3」・・・「DayLabel42」を配置していきます。(ラベルはコピー&ペースト操作が可能ですがオブジェクト名は各々変更が必要です。CtrlやShiftキーを押しながらクリックすることで、複数選択も可能です。)日曜の列にあたる「DayLabel1」「DayLabel8」「DayLabel15」・・・には、ForeColorを日曜の色と同じ設定に変更し、土曜の列にあたる「DayLabe7」「DayLabe14」「DayLabe21」・・・ には、ForeColorを土曜の色と同じ設定に変更します。(1行目を作成して、一週間分の一行を選択し、コピー&ペーストで6列作成したり、日曜日のラベルを縦一列全選択して、プロパティのForeColorをすべて同色に設定することも可能です。)日付はマクロの中で制御するため、初期値はなんでも構いません。2桁を設定しておくと、必要な幅がわかります。図では、すべてのCaptionに30を設定しています。

「DayLabel数値」は、マクロから日付を入力したり、不要な箇所を空欄に変更する制御を行うため、すべてのラベルにきちんとオブジェクト名を設定してください。

曜日ラベル

フォームに設定するオブジェクトは以上になります。

カレンダーフォームの作成 - コード側

左側のプロジェクトエクスプローラのCalenderFormを右クリックして、コードの表示を選択します。

曜日ラベル

開いたシートに、コードを記述していきます。

UserFormオブジェクトには、Initializeイベントが用意されており、一度だけ実行されます。(初期化処理を記述する。)

 
'クラス型の変数を配列定義
Private myDayLabel(0 To 42) As New DayLabelClass

Private Sub UserForm_Initialize()

Dim cnt As Integer

'すべての日付ラベルに対して、
For cnt = 0 To 41

'クリック時のイベントをクラス型変数に登録
myDayLabel(cnt).myLabelClass CalenderForm.Controls("DayLabel" & cnt + 1)

Next

'今月の年月で初期化
MonthNow_Click

End Sub

DayLabelClassは、作成しているクラスモジュールになります。日付をクリックされたときのイベントを「DayLabel1_Click()」「DayLabel2_Click()」…と、42モジュール作成するのは大変なため、イベントを受け取れるクラス型を定義し、そのクラス型の配列にDayLabel1~DayLabel42までのイベントを登録しています。

MonthNow_Clickは、「今月に戻る」ラベルをクリックされた場合処理になりますが、カレンダーに表示させる年月として、今月の年月を設定します。

 
Private Sub MonthNow_Click()

Dim t_year As Integer
Dim t_month As Integer

'今年
t_year = year(Now)

'今月
t_month = Month(Now)

'ラベルに今年今月のカレンダー値を設定
Call SetMonthDate(t_year, t_month)

'「今月に戻る」ラベルを非表示にする
MonthNow.Visible = False
End Sub

Nowと記述するとその日の日付情報を受け取ることができます。

 
'*******************************************************
' 引数で指定された年と月の情報から、
' カレンダーを作成する。
' 日付のラベルに対して、不要なラベルは非表示として、
' 日付のラベルの表示文字列(.Caption)に日付を設定する。
' 年月が今月の場合、今日の日付に対して、背景色を変更する
'*******************************************************
Private Function SetMonthDate(disp_year As Integer, disp_month As Integer)

Dim t_week As Integer '表示月ついたちの曜日(数値)
Dim t_end_date As Date '表示月の最終日(日付)
Dim t_end As Integer '表示月の最終日(数値)

Dim cnt As Integer 'ラベルカウンタ
Dim set_day As Integer '日付カウンタ

'年月表示ラベルに設定
MonthLabel.Caption = disp_year & "年" & disp_month & "月"
'グローバル変数(disp_day)に表示中の年月情報を保存
disp_day = disp_year & "/" & disp_month & "/1"

'2024/05/13 バグ対応↓
'「今月に戻る」ラベルの表示・非表示を制御する
If disp_year = Year(Now) And disp_month = Month(Now) Then
'今月の場合
'「今月に戻る」ラベルを非表示にする
MonthNow.Visible = False
Else
'今月ではない場合

'「今月に戻る」ラベルを表示する
MonthNow.Visible = True
End If
'2024/05/13 バグ対応↑

'今月一日の曜日
t_week = Weekday(disp_year & "/" & disp_month & "/" & 1)


'表示に不要なラベルを空欄で初期化
'DayLabel1~t_week-1まで使用しない
For cnt = 1 To t_week - 1
With CalenderForm.Controls("DayLabel" & cnt)
.Caption = ""
End With
Next

'DateSerial(年,月,日)
'日にちに0を設定すると前の月の最終日に自動調整される
'末日の取得
t_end_date = DateSerial(disp_year, disp_month + 1, 0)
t_end = Day(t_end_date)

'ラベルの最後(カレンダーのはじまる曜日分ずらす)
t_end = t_end + t_week - 1

'ラベルへ日にちの設定
set_day = 1

'カレンダーの日付をラベルに設定
For cnt = t_week To t_end

With CalenderForm.Controls("DayLabel" & cnt)
.Caption = set_day
End With

'今日と一致
If year(Now) = disp_year Then
If Month(Now) = disp_month Then
If Day(Now) = set_day Then
' 今日と一致する日は、背景色と枠線を変更

With CalenderForm.Controls("DayLabel" & cnt)
.BorderStyle = fmBorderStyleSingle
.BackColor = RGB(CInt("&H" & "FF"), CInt("&H" & "F1"), 0)
End With

End If
End If
End If

set_day = set_day + 1

Next

'残ったラベルに空欄設定
For cnt = t_end + 1 To 42 'ラベル定義最後

With CalenderForm.Controls("DayLabel" & cnt)
.Caption = ""
End With

Next

End Function

上記内で使用されているdisp_dayは、グローバル変数として定義しています。
Public disp_day As Date

「>>」ボタンが押された場合の処理。カレンダーの次の月を表示させる。

 
Private Sub NextMonth_Click()

Dim t_year As Integer
Dim t_month As Integer
Dim next_month As Date

'ラベルの初期化
' (今日のラベルに背景色変更を入れているため)
Call Init_label

'表示済みの年月
t_year = year(disp_day)
t_month = Month(disp_day)

'次の月を取得 データ補正してくれる
next_month = DateSerial(t_year, t_month + 1, 1)

'取得しなおす
t_year = year(next_month)
t_month = Month(next_month)

Call SetMonthDate(t_year, t_month)

'2024/05/13 バグ対応 ここに記述していた「今月に戻る」ラベルの
'表示・非表示制御は、SetMonthDate関数の中に移動

End Sub

上記内で使用されているdisp_dayは、グローバル変数として定義しています。
Public disp_day As Date

標準関数のDateSerialは、13月と入力しても、次の年の1月にデータを補正してくれます。

Init_labelについては、下記に記述します。

SetMonthDateについては、このページ内に記載済みです。

 
Private Function Init_label()

Dim cnt As Integer

'Label背景と枠線の初期化
For cnt = 1 To 42 'ラベル定義最後

With CalenderForm.Controls("DayLabel" & cnt)

.BorderStyle = fmBorderStyleNone 'なし
.BackColor = vbButtonFace '初期値'&H8000000F&

End With

Next

End Function

「<<」ボタンが押された場合の処理。カレンダーの前の月を表示させる。

 
Private Sub PreMonth_Click()

Dim t_year As Integer
Dim t_month As Integer
Dim pre_month As Date

'ラベルの初期化
' (今日のラベルに背景色変更を入れているため)
Call Init_label

'表示済みの年月
t_year = year(disp_day)
t_month = Month(disp_day)

'次の月を取得 データ補正してくれる
pre_month = DateSerial(t_year, t_month - 1, 1)

'取得しなおす
t_year = year(pre_month)
t_month = Month(pre_month)

Call SetMonthDate(t_year, t_month)

'2024/05/13 バグ対応 ここに記述していた「今月に戻る」ラベルの
'表示・非表示制御は、SetMonthDate関数の中に移動

End Sub

上記内で使用されているdisp_dayは、グローバル変数として定義しています。
Public disp_day As Date

標準関数のDateSerialは、0月と入力しても、前の年の12月にデータを補正してくれます。

Init_labelについては、上記に記述しています。

SetMonthDateについても、このページ内に記載済みです。

ユーザが×ボタンで閉じた場合の動作

<カレンダーを頻繁に閉じる場合は>

×ボタンで閉じたときの処理を追加しない場合、毎回メモリが解放され、再度呼び出された時に初期状態から開始するため、必ず今月のカレンダーから表示が始まります。前回表示させた年月を保持させるために、フォームを終了させずに、非表示にする。(下記コードのMe.Hide及びCancel=Trueの箇所)

<カレンダーを基本出し続けることが前提の場合は>

メモリが解放されるように、Unload Meとする。

作業するエクセルファイルを変えるときは一度×ボタンで、カレンダーを削除し、再度作業したいエクセルファイル上で、マクロの実行から呼び出すことで、エクセルファイルの操作中に別エクセルが上に表示されたり、カレンダーが隠れることを防げます。

 
' ***********************************
' ユーザが×ボタンで閉じた場合の処理
' ***********************************
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)

If CloseMode = 0 Then '×ボタンを押された場合

'5/30 Unload Meに変更
'Me.Hide
'Cancel = True
Unload Me

End If
End Sub

日付ラベルをクリックされた時の動作

クラスモジュールにDayLabelClassを作成します。

クラスモジュールの作成。DayLabelClass

 
'ラベルイベントを定義
Public WithEvents DayLabels As MSForms.label

'フォーム初期化時に呼び出される。ラベルオブジェクトを設定
Public Sub myLabelClass(setlabel As MSForms.label)

'変数にlabelを格納
Set DayLabels = setlabel

End Sub
'******************************
' ラベルがクリックされたとき
'******************************
Private Sub DayLabels_Click()

Dim select_year As Integer
Dim select_month As Integer

'現在カレンダーで表示している年月を取得
select_year = year(CalenderForm.disp_day)
select_month = Month(CalenderForm.disp_day)

'クリックされたラベルに値(数字)がある場合
If Not DayLabels.Caption = "" Then

'選択中のセルに日付情報を設定
ActiveCell.Value = select_year & "/" & select_month & "/" & DayLabels.Caption

'カレンダーを非表示にしないで表示したままにする
'CalenderForm.Hide

End If

End Sub

カレンダーフォームの初期化処理でDayLabelClass型配列に日付ラベルを登録をしています。

☆☆☆注意事項☆☆☆

別のブックでカレンダーを開きたい場合は、一度×で閉じてから、再度別のブックの開発タブ「マクロの実行」から呼び出してください。

表示しているカレンダーの年月を維持したまま、別ブックでも使用したい場合は、「UserForm_QueryClose」関数内で、「Unload Me」を実行せずに、「Me.Hide」と「Cancel= True」を実行して×ボタンでも情報を維持させます。そのうえで、複数エクセルブックでカレンダーの付け替え対応マクロに記載した対処が必要です。

このページからダウンロードできるものは、複数ブックでのカレンダー操作に対応はしていません。

複数ブックでカレンダーを共有して操作したい方は、自作カレンダーフォームをショートカットキーで呼び出そうからダウンロードし、日付入力時に呼び出される「DayLabels_Click」で「CalenderForm.Hide」の実行をやめれば、出しっぱなしになります。

「自作カレンダーフォームをショートカットキーで呼び出そう」版でも、開発タブのマクロの実行から呼び出すことは可能です。

健忘録を兼ねた整理(カレンダー(フォーム全般)がエクセルの後ろに隠れる原因)

Hide(Unload Cancel)は、情報を保持しつづけます。Cancel = True は、UnloadをCancelする設定のため、Hideとセットで使用します。

 この記事のカレンダーは、ひとつのブックから呼び出し、必要な間は、カレンダーを出しっぱなしにして、不要になったら、Unloadですべて初期化しています。別ブックでカレンダーを出したいときは、再度、別ブックからカレンダーを呼び出すことで、「カレンダーが紐づけられているブック」も更新されるため、エクセル操作中にカレンダーが後ろに隠れることはありません。だだし、②の最後に表示していた年月は初期化されます。

 UnloadせずにHide(Unload Cancel)をして、別ブックからマクロの実行によって、呼び出しても①の過去にどのブックが呼び出されたかを覚えているため、カレンダーは、新しく呼び出したブックを操作するとカレンダーは後ろに隠れることになります。

 ショートカットキーを使ってカレンダーを呼び出すケースでは、日付を一つ入力するか、×ボタンが押されたら、カレンダーを消しています。②の情報維持を優先しているため、①の呼び出されたブックを覚えています。対処方法として、「呼び出しブックが変わったか?」をマクロにて確認して、表示中の年月(フォームが保持している情報)を保存し、カレンダフォームの再起動後、表示するカレンダーを保存した年月で実行することで、エクセルブックとカレンダーの紐づけしなおしをサポートしています。それが、複数エクセルブックでカレンダーの付け替え対応マクロになります。

このカレンダーに自分の予定を追加して表示する方法

マウスを日付に近づけると登録した予定を表示できるように追加対応したものがあります。

参考までにどうぞ。

エクセルマクロ有効ファイルのダウンロード

操作手順

下記からダウンロードしたエクセルファイルを起動し、コンテンツの有効化を行う。

マクロの有効化手順については、こちらも参考にしてください。

作業する別のエクセルファイル(xlsx)上で開発タブのマクロから手動で実行する。

注意事項は読んでおいてください。

カレンダーフォーム手動呼び出し版(xlsm)のダウンロード

自作のカレンダーフォームで祝日を赤色に変更する追加処置

上記のエクセルマクロを取り込んだうえで、下記の追加処置をしています。

ラベルのフォントカラーを変更するマクロ

CalenderFormオブジェクト内に下記を追加記述します。

 
Private Sub SetFontColor(cnt As Integer, chkDay As Date)

Dim dlabel As MSForms.Label

Set dlabel = CalenderForm.Controls("DayLabel" & cnt)

'土曜なら青 日曜なら赤 それ以外は黒で一旦初期化
If Weekday(chkDay) = vbSaturday Then
dlabel.ForeColor = RGB(0, 0, 255) '青

ElseIf Weekday(chkDay) = vbSunday Then
dlabel.ForeColor = RGB(255, 0, 0) '赤

Else
dlabel.ForeColor = RGB(0, 0, 0) '黒

End If

'ラベルにマウスオーバー時、表示するテキストの初期化
dlabel.ControlTipText = ""

If 祝日判定(chkDay) <> "" Then
'祝日は赤
dlabel.ForeColor = RGB(255, 0, 0) '赤
'ラベルにマウスオーバー時、祝日名を表示する
dlabel.ControlTipText = HolidayName

End If

End Sub

ラベルのフォントカラーを変更するマクロの解説

引数cntは、ラベル番号を受け取っています。引数chkDayは祝日であるか確認する日付です。

土曜なら青、日曜なら赤、それ以外の平日は黒に設定します。その後、祝日判定を呼び出し、祝日ならFontを赤色に変更しています。

ラベルのContrlTipTextに祝日名を設定すると、マウスを近づけたときに、祝日名が表示されます。

下記の図は、7/17にマウスのカーソルをのせています

自作カレンダー祝日表示マウスオーバーでコメント表示

上記を呼び出す箇所を以下に追加します。

ラベルに日付を入力する関数SetMonthDateから追加呼び出し

フォントカラーを設定する関数SetFontColorの呼び出しを以下に追加します。

 
Private Function SetMonthDate(disp_year As Integer, disp_month As Integer)

Dim t_week As Integer

Dim t_end_date As Date
Dim t_end As Integer

Dim cnt As Integer
Dim set_day As Integer

'年月表示ラベルに設定
MonthLabel.Caption = disp_year & "年" & disp_month & "月"
disp_day = disp_year & "/" & disp_month & "/1"


'一日の曜日
t_week = Weekday(disp_year & "/" & disp_month & "/" & 1)


'DayLabel1~t_week-1までに空白を設定
For cnt = 1 To t_week - 1

With CalenderForm.Controls("DayLabel" & cnt)
.Caption = ""
End With

Next

'DateSerial(2017, 10, 0) → 2017/09/30(自動調整された)
'末日の取得
t_end_date = DateSerial(disp_year, disp_month + 1, 0)
t_end = Day(t_end_date)

'ラベルの最後
t_end = t_end + t_week - 1

'ラベルへ日にちの設定
set_day = 1
For cnt = t_week To t_end

With CalenderForm.Controls("DayLabel" & cnt)
.Caption = set_day
End With

'色の設定
'↓↓↓日付のフォントカラーを変更するように追加対応
Call SetFontColor(cnt, DateSerial(disp_year, disp_month, set_day))

'今日と一致
If year(Now) = disp_year Then
If Month(Now) = disp_month Then
If Day(Now) = set_day Then

With CalenderForm.Controls("DayLabel" & cnt)
.BorderStyle = fmBorderStyleSingle
.BackColor = RGB(CInt("&H" & "FF"), CInt("&H" & "F1"), 0)
End With

End If
End If
End If

set_day = set_day + 1

Next

'残ったラベルに空欄設定
For cnt = t_end + 1 To 42 'ラベル定義最後

With CalenderForm.Controls("DayLabel" & cnt)
.Caption = ""
End With

Next

End Function

祝日対応版エクセルマクロ有効ファイルのダウンロード

注意事項は読んでおいてください。

祝日のメンテナンス方法については、国民の祝日を取得するエクセルマクロ(Excel VBA)にて確認をしてください。

カレンダーフォーム手動呼び出し祝日対応版(xlsm)のダウンロード

前の項目 - 別ファイルへシートコピー後、シート名変更をして保存する
次の項目 - 自作カレンダーフォームをショートカットキーで呼び出そう