[旧版] ExcelVBAでカレンダーコントロールを自作する

[旧版] ExcelVBAでカレンダーコントロールを自作する

Office2003だった頃、Access付属のmscal.ocxというカレンダーコントロールを使ってたのですが…。2010からもう使えないとのこと…!!どうにか似たようなことは出来ないものかと思って、つくってみました。


こちらのカレンダーは、旧版です

  • 新しいバージョンを公開しました。たくさんコメントいただいているのでこの記事自体は残しておきますが、今後この記事のカレンダーはアップデートしない予定です(2022/10/23)
  • コメント欄でリクエストいただいたので、和暦表示の方法も記事中に追記しました。DL用に用意してあるのは西暦表示のみなので、必要な際は部分的に差し替えてご使用ください。(2014/8/18)

完成図

140804-22

このようなフォームでボタンを押すと、

140804-27

あらかじめテキストボックスに入っていた日付(空欄だったら本日)に色がついたカレンダーフォームが表示され、

140804-28

クリックした日付をテキストボックスに入力します。

ダウンロード

記事中に解説がありますが、クリックされた日付を取得する際にフォームモジュール内で完結する方法(CalenderForm1.xlsm)と、クラスを作ってクリックイベントを利用する方法(CalenderForm2.xlsm)の、ふたつのファイルが入っています。

祝日対応はしておりません。というか、祝日を対応させようとすると結構面倒なので、後述するアドインツールを使わせてもらうのが一番だと思います!

祝日を調べられるAPIを使うという手もありますが、ネット環境がなくても動くのを考えるなら、専用シートをひとつ作って、月と日のマトリクス表を書いて、祝日の日に 1 とか書いてフラグ化しておいて、カレンダーを読み込む際にそこを参照してフラグが立ってたら文字を赤くする、っていう手も、あるかなぁ、と。

これだったら好きな日を赤くできるので、祝日だけじゃなくて会社がお休みの日も設定できたり、結構自由度が高いんじゃないかなぁと。年始めとかに今年の休日を全部設定する、的な手間は必要ですが。

せっかくなのでそちらの方法も解説してみました。

あと、カレンダーではないんですけどこういう記事も書いたのでご参考になれば。

参考サイト

こちらを参考にさせていただいて作りました。厚く御礼申し上げます!

頑張って自作しなくても

こちらのサイトでは、暦や祝日の複雑なロジックを組み込んだカレンダーなどが利用できるアドインツールを公開されています。

2013以降ならば「コンテンツアプリ」というものが利用できます!こちらのアプリは祝日も含まれているし、デザインも可愛くてとっても素敵です♡

やっぱり同じことを考えて自作してる人がいらっしゃいました…! こちらはフォームへのテキストボックスだけでなく、シート上のセルへも直接日付を入れられるので便利そうですね(*´ω`*)

起動ボタンをつくる

それでは、ここからは「自分で作ってみたい!」という方むけ。

140804-01

まずは新規のシートに、後でModule1に書くプロシージャを起動させるボタンを作っておきます。

ボタンの作り方や、起動の結びつけ方はこちらをご参照下さい。

日付選択のフォームをつくる

140804-02

Visual Basic Editorを開いて、新しくユーザーフォームを作ります。

140804-03

UserForm1というのが出来るので、ツールボックスを使って(出ていない場合は「表示」→「ツールボックス」)テキストボックスとボタンをひとつずつ作ります。TextBox1CommandButton1というオブジェクト名で出来るはずなので、そのままにしておいてください。

140804-04

必須ではありませんが、プロパティウィンドウのCaptionで、フォームの表示名を変更できます。(出ていない場合は、「表示」→「プロパティウィンドウ」)

モジュールをつくる

140804-05

今作ったフォームを表示させるための標準モジュールを新しく作ります。

140804-06

Module1が出来たので、こう書きます。

'---カレンダー用変数
Public clndr_date As Date 'テキストボックスの値を格納する変数
Public clndr_flg As Boolean 'カレンダーがクリックされたか判定するフラグ

Sub start()
  UserForm1.Show 'メインフォームを開く
End Sub

2,3行目は、これからユーザーフォームを跨いで使うためPublicで宣言しておきます。

上記のコードではテキストボックスを空欄で表示しますが、初期値として何かの日付を入れておくことができます。例えば本日の日付をあらかじめ入れておきたい場合は、

Sub start()
  With UserForm1
    .TextBox1 = Format(Date, "yyyy/mm/dd") '日付を入れる(西暦)
    '.TextBox1 = Format(Date, "ggge年m月d日") '日付を入れる(和暦)
    .Show 'ユーザーフォームを表示
  End With
End Sub

3, 4行目のように書いてからフォームを表示します。(実際に使う場合はどちらかひとつにしてください。)応用して「本日より1週間前」とか「本日より1週間後」とか、いろいろできます。

この記事ではテキストボックスが空欄のまま進めますが、startというプロシージャを、最初にシートに作ったボタンから起動するように設定すると、

140804-07

このようにユーザーフォームが表示されます。

日付選択フォームのコードを書く

140804-08

さっき作ったUserForm1を右クリックして、「コードの表示」をして、そこにこう書きます。

西暦表示

Private Sub CommandButton1_Click()
  clndr_flg = False 'フラグリセット
  If IsDate(Me.TextBox1) = False Then '日付が入ってなければ
    clndr_date = Date '今日の日付を格納
  Else
    clndr_date = Me.TextBox1 'テキストボックスの日付を格納
  End If
  UserForm2.Show 'カレンダーを開く
  If clndr_flg = True Then Me.TextBox1 = Format(clndr_date, "yyyy/mm/dd") 'クリックされた日付を上書き
End Sub

追記:和暦表示

Private Sub CommandButton1_Click()
  clndr_flg = False 'フラグリセット
  If IsDate(Me.TextBox1) = False Then '日付が入ってなければ
    clndr_date = Date '今日の日付を格納
  Else
    clndr_date = Me.TextBox1 'テキストボックスの日付を格納
  End If
  UserForm2.Show 'カレンダーを開く
  If clndr_flg = True Then Me.TextBox1 = Format(clndr_date, "ggge年m月d日") 'クリックされた日付を上書き
End Sub

CommandButton1を押すと、TextBox1に入っている日付(空の場合は本日)を、さっきModule1でPublic宣言したclndr_dateという変数に入れて、UserForm2を開く、というコードです。(UserForm2にはこの後カレンダーを実装する予定ですが、この時点ではまだ存在しないので動かすとエラーになっちゃいます。)

8行目でカレンダーを開き、それが開かれている間はこのコードは中断します。カレンダーが閉じると、このコードの9行目が実行されるので、そこでクリックされた日付をTextBox1に上書きしてやる、という流れです。

カレンダーを作る

「挿入」→「ユーザーフォーム」から、UserForm2を作ります。

今回は例としてUserForm2にカレンダーを作りますが、もちろん違うフォームでも構いません。オブジェクト名を変えてCalenderFormなど、固有の名前にしておいてテンプレートとして残しておくと、今後必要なときに使いまわすことができて便利かも。
140804-09

ツールボックスのラベルを選んで、

140804-10

試しにひとつ作ります。ここには、カレンダーの日付が入るので、表示確認のために一番大きな数字を入れてみます。

140804-11

プロパティウィンドウで、文字の大きさなどが変えられます。

140804-12

文字をセンタリング(中央寄せ)にしたり、幅や高さも指定できます。

あと、ここでオブジェクト名が確認できるので、このオブジェクトがLabel1であることを確認して下さい。

140804-13

ラベルの中身(Captionというプロパティ)を消して、コピーして横に7個並べます。このとき、オブジェクト名の数値が図のように左から順番に並ぶようにしてください。

140804-14

横に7個並んだら、その1行をコピーして下に更に並べます。これも左から順番になるようにチェックしてください。

わたしの環境だけかもしれませんが、複数コピペしたら8番が一番右にいっちゃったりして、順番通りになるように並べ替えが必要でした。

140804-15

横に7列、縦に6行分になるまで繰り返します。

140804-16

ラベルのオブジェクト番号は、このようになっているはず。

実はこれ、ほんとはLabel37までで足りるなー、と後で気づきました。このあと42個で進めてますが、お好みで37個に読み換えていただいて構いません。
140804-17

では、今度はコンボボックスを選んで、

140804-18

ふたつ設置します。ComboBox1には年が、ComboBox2には月が入る予定です。

140804-19

42個並んだラベルの、最左列を全て選択して、

140804-20

プロパティウィンドウで、文字色を赤にしておきます。同じ手順で、最右列は青にします。

カレンダーフォームのコードを書く

140804-21

UserForm2を右クリックして、「コードの表示」をして、そこにこう書きます。

西暦表示

Private Sub UserForm_Initialize() 'Formが開くとき
  Dim i As Integer
  
  For i = -3 To 3 '前後3年分の年を登録
    Me.ComboBox1.AddItem CStr((Year(clndr_date)) + i)
  Next i
  For i = 1 To 12 '月を登録
    Me.ComboBox2.AddItem CStr(i)
  Next i
  
  Me.ComboBox1 = Year(clndr_date) '年を指定
  Me.ComboBox2 = Month(clndr_date) '月を指定
End Sub

Private Sub ComboBox1_Change() '年が変更されたとき
  Call clndr_set
End Sub

Private Sub ComboBox2_Change() '月が変更されたとき
  Call clndr_set
End Sub

Private Sub clndr_set() 'カレンダーの作成と表示
  Dim yy As Integer, mm As Integer, i As Integer, n As Integer, endDay As Integer
  
  If Me.ComboBox1 = "" Or Me.ComboBox2 = "" Then Exit Sub '年か月どちらか入ってなければ中止
  yy = Me.ComboBox1 '年
  mm = Me.ComboBox2 '月
   
  For i = 1 To 42 'ラベルの初期化
    Me("Label" & i).Caption = ""
    Me("Label" & i).BackColor = Me.BackColor
  Next
  
  n = Weekday(yy & "/" & mm & "/" & 1) - 1 'その月の1日の曜日番号に、マイナス1したもの
  endDay = Day(DateAdd("d", -1, DateAdd("m", 1, yy & "/" & mm & "/" & "1"))) '月末日の算出
  For i = 1 To endDay
    Me("Label" & i + n).Caption = i '日を入れる
    If CDate(yy & "/" & mm & "/" & i) = clndr_date Then Me("Label" & i + n).BackColor = RGB(255, 255, 0) 'TextBoxの日と同じなら色をつける
  Next i
End Sub

Visual Basic Editorにコピペするとわかりますが、4つのブロック(プロシージャ)に分かれています。今回の肝はその月の1日をどの位置から始めるかというところでして、そこの鍵がハイライトしてある35行目です。

指定日の曜日を 日→1, 月→2, … 土→7 のように、数値で取得してくれるWeekday()という関数を利用して、ComboBox1ComboBox2から「年/月/1」と合成した、その月の1日の曜日番号を取得します。これを -1 したものを日付に足せば、曜日分ずらしたラベルの位置が割り出せるんです。

このアイデアは、冒頭で紹介させていただいた参照サイトより、お知恵を拝借しました!ありがとうございますー!

追記:和暦表示

Private Sub UserForm_Initialize() 'Formが開くとき
  Dim i As Integer
  
  For i = -3 To 3 '前後3年分の年を登録
    Me.ComboBox1.AddItem Format(DateAdd("yyyy", i, clndr_date), "ggge")
  Next i
  For i = 1 To 12 '月を登録
    Me.ComboBox2.AddItem CStr(i)
  Next i
  
  Me.ComboBox1 = Format(clndr_date, "ggge") '年を指定
  Me.ComboBox2 = Month(clndr_date) '月を指定
End Sub

Private Sub ComboBox1_Change() '年が変更されたとき
  Call clndr_set
End Sub

Private Sub ComboBox2_Change() '月が変更されたとき
  Call clndr_set
End Sub

Private Sub clndr_set() 'カレンダーの作成と表示
  Dim yy As String, mm As Integer, i As Integer, n As Integer, endDay As Integer
  
  If Me.ComboBox1 = "" Or Me.ComboBox2 = "" Then Exit Sub '年か月どちらか入ってなければ中止
  yy = Me.ComboBox1 '年
  mm = Me.ComboBox2 '月
   
  For i = 1 To 42 'ラベルの初期化
    Me("Label" & i).Caption = ""
    Me("Label" & i).BackColor = Me.BackColor
  Next
  
  n = Weekday(yy & "年" & mm & "月" & "1日") - 1 'その月の1日の曜日番号に、マイナス1したもの
  endDay = Day(DateAdd("d", -1, DateAdd("m", 1, yy & "年" & mm & "月" & "1日"))) '月末日の算出
  For i = 1 To endDay
    Me("Label" & i + n).Caption = i '日を入れる
    If CDate(yy & "年" & mm & "月" & i & "日") = clndr_date Then Me("Label" & i + n).BackColor = RGB(255, 255, 0) 'TextBoxの日と同じなら色をつける
  Next i
End Sub

ハイライトされてる部分が、西暦表示と違うところです。和暦は「平成」などの文字列がつくので、それに合わせて「年」「月」「日」を使って日付を生成します。

では、文章だけだとイマイチ分かりにくいので、動作確認してみましょう!

140804-22

起動させて、このボタンを押してみると…

140804-23

カレンダーが表示されました! 月を変更すると、日付や曜日が対応して表示されるはずです。UserForm1のテキストボックスで指定されていた日(空欄だった場合は本日)に色がつくようになっていますが、これはさっきのコードの39行目で色を変えられます。(RGB(255, 255, 0)は黄色です。)

140804-24

見栄えを整えました。このサンプルは文字の大きさが11, 各ラベルのWidthが18, Heightが12, 指定日の背景色はRGB(189, 231, 255)にしています。

もちろんお好みでいいんですが、最初からこの大きさで作れば良かったなぁと軽く後悔したので、よろしければご参考になさってもらえたら_(´ω`_)⌒)_

コンボボックスの後ろに付けた「年」「月」と、曜日の「日~月」はラベルで作っています。Label1~42は日付部分で使うので、43以降の数値になるように作ってください。

クリックされた日付を取得する

さて、それっぽくなってきましたが、このままだとただカレンダーが表示されるだけで、日付をクリックしても何も起こりません。クリックされた日付を取得する方法は、2つあります。

方法1

さっきのUserForm2のコードの後ろに、これを追記します。

西暦表示

'上略

Private Sub Label1_Click()
  If Me.Label1.Caption = "" Then Exit Sub 'ラベルが空だったら中止
  clndr_date = Me.ComboBox1 & "/" & Me.ComboBox2 & "/" & Me.Label1.Caption '日付を生成して変数に格納
  clndr_flg = True 'フラグを立てる
  Unload Me 'カレンダーを閉じる
End Sub

追記:和暦表示

'上略

Private Sub Label1_Click()
  If Me.Label1.Caption = "" Then Exit Sub 'ラベルが空だったら中止
  clndr_date = Me.ComboBox1 & "年" & Me.ComboBox2 & "月" & Me.Label1.Caption & "日" '日付を生成して変数に格納
  clndr_flg = True 'フラグを立てる
  Unload Me 'カレンダーを閉じる
End Sub

Label1をクリックされたときに起動する、クリックイベントというやつです。日付が入っていないラベルもあるので、空なら中止というエラー処理の後、予めPublic宣言してあったclndr_dateという変数に年と月を合成した日付を格納してカレンダーを閉じます。

これだけ見ると簡単なのですが、問題は、これをLabel○の数値部分を変えて、ラベルの数だけ書かなければならないということ…!42個!!!

'上略

Private Sub LabelClick(ByVal i As Integer)
  If Me("Label" & i).Caption = "" Then Exit Sub 'ラベルが空だったら中止
  clndr_date = Me.ComboBox1 & "/" & Me.ComboBox2 & "/" & Me("Label" & i).Caption '日付を生成して変数に格納
  clndr_flg = True 'フラグを立てる
  Unload Me 'カレンダーを閉じる
End Sub

Private Sub Label1_Click()
  Call LabelClick(1)
End Sub

Private Sub Label2_Click()
  Call LabelClick(2)
End Sub

Private Sub Label3_Click()
  Call LabelClick(3)
End Sub

'ラベルの数だけ続く↓

処理部分を分離してみました。(和暦にしたい場合は5行目を差し替えてください。)42個書くことには変わらないですが、ちょっとは省コードになるかと。

ラベル数全てベタ書きは気持ち悪いですが、ひとつのユーザーフォームだけでカレンダーが完結するので、ひとつ作っておいてコピーして使い回したい時なんかにはこっちのが楽ですね。

方法2

とはいえ42個も…書きたくないな…_(:3 」∠)_ と思うのはもっともですよねw クラスを作って共通化させて書く、という方法もあります!

UserForm2のコードに、ハイライト部分を追記します。(和暦も同じです。)

Private ctrl(1 To 42) As New Class1

Private Sub UserForm_Initialize() 'Formが開くとき
  Dim i As Integer
  
  For i = -3 To 3 '+/-3年分の年を登録
    Me.ComboBox1.AddItem CStr((Year(clndr_date)) + i)
  Next i
  For i = 1 To 12 '月を登録
    Me.ComboBox2.AddItem CStr(i)
  Next i
  For i = LBound(ctrl) To UBound(ctrl) 'ラベルのクリックイベントを拾うための処理
    ctrl(i).SetCtrl Me("Label" & i)
  Next
  
  Me.ComboBox1 = Year(clndr_date) '年を指定
  Me.ComboBox2 = Month(clndr_date) '月を指定
End Sub

'下略

ここで書いた「1から42」という番号のラベルのみ、クリックイベントを拾います。先ほどのサンプルだと、「年」「月」、曜日の「日~月」というラベルは43番以降なので、これらをクリックしても何も起こりません。

140804-25

クラスモジュールを新規作成します。

140804-26

新しく出来たClass1に、以下のコードを書きます。

西暦表示

Private WithEvents Target As MSForms.Label
 
Public Sub SetCtrl(new_ctrl As MSForms.Label)
  Set Target = new_ctrl
End Sub

Private Sub Target_Click()
  With UserForm2
    If Target.Caption = "" Then Exit Sub 'ラベルが空だったら中止
    clndr_date = .ComboBox1 & "/" & .ComboBox2 & "/" & Target.Caption '日付を生成して変数に格納
  End With
  clndr_flg = True 'フラグを立てる
  Unload UserForm2 'カレンダーを閉じる
End Sub

追記:和暦表示

Private WithEvents Target As MSForms.Label
 
Public Sub SetCtrl(new_ctrl As MSForms.Label)
  Set Target = new_ctrl
End Sub

Private Sub Target_Click()
  With UserForm2
    If Target.Caption = "" Then Exit Sub 'ラベルが空だったら中止
    clndr_date = .ComboBox1 & "年" & .ComboBox2 & "月" & Target.Caption & "日" '日付を生成して変数に格納
  End With
  clndr_flg = True 'フラグを立てる
  Unload UserForm2 'カレンダーを閉じる
End Sub

細かい解説はこちらの記事をご参照ください。

動作検証

140804-27

起動させて、カレンダーの20日をクリックしてみます。

140804-28

カレンダーが閉じて、今クリックした日付がテキストボックスに入れば成功です!

140804-29

なお、複数のテキストボックスでカレンダーを使いたい! という場合は、カレンダーの呼び出し/受け取り部分を以下のように対象のテキストボックスを引数にして別プロシージャに切り分けます。

Private Sub CommandButton1_Click()
  Call putCalenderDate(Me.TextBox1)
End Sub
 
Private Sub CommandButton2_Click()
  Call putCalenderDate(Me.TextBox2)
End Sub
 
Private Sub CommandButton3_Click()
  Call putCalenderDate(Me.TextBox3)
End Sub

別プロシージャになった部分は、標準モジュール(ここでは最初に書いたModule1の末尾)に、テキストボックスを受け取る形で書きます。

Public clndr_date As Date 'テキストボックスの値を格納する変数
Public clndr_flg As Boolean 'カレンダーがクリックされたか判定するフラグ

Sub start()
  UserForm1.Show 'メインフォームを開く
End Sub

Sub putCalenderDate(ByVal tgtTextBox As MSForms.TextBox)
  clndr_flg = False 'フラグリセット
  If IsDate(tgtTextBox) = False Then '日付が入ってなければ
    clndr_date = Date '今日の日付を格納
  Else
    clndr_date = tgtTextBox 'テキストボックスの日付を格納
  End If
  CalenderForm.Show 'カレンダーを開く
  If clndr_flg = True Then tgtTextBox = Format(clndr_date, "yyyy/mm/dd") 'クリックされた日付を上書き
End Sub

標準モジュールに書くことで、どのフォームからも、ひとつのカレンダーを使いまわせるようになります。

140804-30

こんな感じで、それぞれ違う日付をカレンダーで取得できます。何日~何日、みたいに複数の日付を指定するとき、カレンダーが見えると曜日とか営業日とか考えながら日付を選べて結構便利なんですよねー。

おまけ

140804-31

カレンダーにスピンボタンをつけてやって、

140804-32

場所はお好みですがこのあたりとか。

140804-21

で、カレンダーを描いたフォーム(今回の例ではUserForm2)のコードに、

西暦表示

Private Sub SpinButton1_SpinUp() 'ひと月戻る
  If Me.ComboBox2 = 1 Then '1月だったら
    Me.ComboBox1 = Me.ComboBox1 - 1 '年-1
    Me.ComboBox2 = 12 '12月へ
  Else
    Me.ComboBox2 = Me.ComboBox2 - 1
  End If
End Sub
 
Private Sub SpinButton1_SpinDown() 'ひと月進む
  If Me.ComboBox2 = 12 Then '12月だったら
    Me.ComboBox1 = Me.ComboBox1 + 1 '年+1
    Me.ComboBox2 = 1 '1月へ
  Else
    Me.ComboBox2 = Me.ComboBox2 + 1
  End If
End Sub

追記:和暦表示

Private Sub SpinButton1_SpinUp() 'ひと月戻る
  If Me.ComboBox2 = 1 Then '1月だったら
    Me.ComboBox1 = Format(DateAdd("yyyy", -1, Me.ComboBox1 & "年1月1日"), "ggge") '年-1
    Me.ComboBox2 = 12 '12月へ
  Else
    Me.ComboBox2 = Me.ComboBox2 - 1
  End If
End Sub
 
Private Sub SpinButton1_SpinDown() 'ひと月進む
  If Me.ComboBox2 = 12 Then '12月だったら
    Me.ComboBox1 = Format(DateAdd("yyyy", 1, Me.ComboBox1 & "年1月1日"), "ggge") '年+1
    Me.ComboBox2 = 1 '1月へ
  Else
    Me.ComboBox2 = Me.ComboBox2 + 1
  End If
End Sub

こんなコードを追加してやると、

140804-33

こんなボタンが実装されて地味に便利ですよ!!というおまけ。

以上です!どなたかのお役に立てたら幸いですー!

公開日:2014/08/04
更新日:2019/08/01

32件のコメント

  1. ウエダ より:

    VBAを始めて1年弱の新参者です。大変わかりやすい解説で理解が深まり感謝しております。他のサンプルコードの解説や書籍を読みあさっていますが、変数の理解が今ひとつです。
    カレンダーコントロールについてですが、テキストボックスやセルの貼り付けなど西暦から和暦にすることはできますが、カレンダーコントロールの年表示が、なぜか西暦ばかりで、特にコンボボックスに格納されている西暦を和暦にすることができません。機会がございましたら、コンボボックス・コンボボックスに格納されている西暦を和暦にする方法を記載していただけないでしょうか?

    • *you より:

      ウエダさん、コメントありがとうございます。

      わたしも和暦は一度も使ったことがないです!でも公的機関はみんな和暦ですもんね。本文に追記してみましたので、お試し下さい。

  2. sota より:

    私も小さな会社でこつこつと自作のプログラムで
    業務の効率化をはかっております。
    大変参考になりました。ありがとうございました。

    • *you より:

      sotaさん、コメントありがとうございます!お役に立てて光栄ですー(*´ω`*)

  3. sota より:

    これからも頑張って下さい(^o^)/
    PS私も子育てと仕事を楽しんでます(*^_^*)

    • *you より:

      ありがとうございます!sotaさんもお子さんがいらっしゃるんですね!一緒にがんばりましょうー\\└(‘ω’)┘//

  4. tuka より:

    A列でダブルクリックするとここで紹介された自作カレンダーが表示され、クリックしたラベルの日付がA列の任意のセルに入力されるようにするにはどうしたら良いかご教授願います。

    • *you より:

      tukaさん、コメントありがとうございます。まずはA列に限らず、シート上のセルをダブルクリックしたらカレンダーが表示されてそのセルに日付が入る、という内容から解説します。日付選択のフォームは要らなくて、カレンダーが書いてあるフォームだけあれば大丈夫です。

      こちらの記事を参考に、実装したいシートモジュールに、

      Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
        Dim tgt As Range: Set tgt = Target.Resize(1, 1) '結合セルだった場合左上を取得
        Call showCalender(tgt) 'カレンダーを表示
      End Sub
      

      標準モジュールに、

      Public clndr_date As Date
      Public clndr_flg As Boolean
      
      Sub showCalender(tgt As Range)
        clndr_flg = False 'フラグリセット
        Debug.Print tgt.Value
        If IsDate(tgt.Value) = False Then '日付が入ってなければ
          clndr_date = Date '今日の日付を格納
        Else
          clndr_date = tgt '日付を格納
        End If
        CalenderForm.Show 'カレンダーを開く
        If clndr_flg = True Then tgt = Format(clndr_date, "yyyy/mm/dd") 'クリックされた日付を上書き
      End Sub
      

      と書けば実装できます。(例はフォーム名が CalenderForm の場合です。)

      それで、「このセル限定」とか「A列で」という条件をつける場合は、さっきのシートモジュールへ、

      Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
        Dim tgt As Range: Set tgt = Target.Resize(1, 1) '結合セルだった場合左上を取得
        If tgt.Address = Range("A1").Address Then 'ダブルクリックしたのがA1セルだったら
          Call showCalender(tgt) 'カレンダーを表示
        End If
      End Sub
      

      とか、

      Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
        Dim tgt As Range: Set tgt = Target.Resize(1, 1) '結合セルだった場合左上を取得
        If tgt.Column = 1 Then 'ダブルクリックしたのがA列だったら
          Call showCalender(tgt) 'カレンダーを表示
        End If
      End Sub
      

      このように条件をつけます。お試しください。

  5. hhgm より:

    startというプロシージャを、最初にシートに作ったボタンから起動するように設定すると、

    この部分の設定の仕方がわかりません、教えていただきたいです

    • *you より:

      hhgmさん、2項目上の「起動ボタンをつくる」の部分をご覧ください。

  6. SAT より:

    商用に使えますか?

    • *you より:

      SATさん、コメントありがとうございます。

      商用でも使っていただいて構いませんが、無保証です。コンテンツの扱いと免責事項をご一読ください。元々自分用に作ったものでエラー処理が甘いので、とりあえず、コンボボックスに日付として認識されない数や文字列が直接入力された場合のエラー処理だけ追記しておきます。

      Private Sub ComboBox1_Change() '年が変更されたとき
        If Me.ComboBox2 <> "" And IsDate(Me.ComboBox1 & "/" & Me.ComboBox2 & "/" & 1) = False Then
          MsgBox "不正な日付が指定されました"
          Exit Sub
        End If
        Call clndr_set
      End Sub
       
      Private Sub ComboBox2_Change() '月が変更されたとき
        If IsDate(Me.ComboBox1 & "/" & Me.ComboBox2 & "/" & 1) = False Then
          MsgBox "不正な日付が入力されました"
          Exit Sub
        End If
        Call clndr_set
      End Sub
      

      カレンダーが置いてあるフォームの、コンボボックスのChangeイベントを上記に書き換えてください。あとは、私の気づかないものもあるかもしれませんので、どんな使われ方をするかテストしてエラーを潰してもらってからのほうが良いと思います。

    • SAT より:

      *youさんありがとうございました。
      参考にデバッグさせて頂きます。

  7. 野田智也 より:

    こんにちは。
    同じ思いで何かないものかと悩んでいたものです。
    大変参考になりました。本当にありがとうございます。
    どうしてもカレンダーが必要だったので、助かりました。
    モジュールも含め、詳細な解説は、とても参考になりました。
    また何か困ったことがあれば、参考にさせて頂いてもいいでしょうか?よろしくお願いします。

    • *you より:

      野田さん、嬉しいコメントありがとうございます! Excelタグで見てもらえれば、私の今までのTipsが詰め込んでありますので、ぜひお役立てくださいませ(*゚ω゚*)

  8. マロンブック より:

    新たに別のフォームを作成して、コンボボックスをクリックするとこのカレンダーが表示されて、指定した列に入力・・・みたいな事は可能なのでしょうか? もし可能であればご教授お願いします。

    • *you より:

      マロンブックさん、コメントありがとうございます。すみませんがちょっと仕様が想像ができません。

      • 「新たに別のフォームを作成して」>「新たに」とは、どこから、なんのきっかけで起動する、どんな内容のフォームでしょうか?
      • 「コンボボックスをクリックすると」>これは「新たに作成されたフォーム」上にあるコンボボックス、ということでよろしいでしょうか。サンプルで示してあるカレンダーの起動はコマンドボタンで、コンボボックスではありません。(左にテキストボックスを並べてあるのでちょっと紛らわしいですね。)
      • 「指定した列に入力」>これは、シート上のセルに日付が入るということでしょうか。最終的にセルに入れるならフォームを経由しなくてもよい気がします。

      他の方のコメントへの返信で、「シート上のセルをダブルクリックしたらカレンダーが表示されてそのセルに日付が入る」という方法を紹介していますので、そちらが参考になるかもしれません。そこに「列限定」も書いてありますが、いかがでしょうか。

  9. マロンブック より:

    返信遅れて申し訳ございません。
    例えば、こちらで紹介されたカレンダーを使用して横5列に異なる日付を入力したいとします。
    しかし、他の方が仰っていましたセルをダブルクリックして日付を入力する方法を私も試してみたのですが、入力する箇所が多くなるとそれも大変かなと思ったのです。
    そこで、フォームを新たに作成しコマンドボタンを押したらカレンダーが表示され、そのカレンダーの日付をクリックすると日付がテキストボックスに表示されて「登録ボタン」を押すと横5列に一気に日付が入力される・・・みたいな感じです。

    • *you より:

      すみません、やはり理解が難しいです。

      カレンダーで指定する日付は1つで、横n列に一気に日付が入力されるということならば、すべて同じ日付が入ってしまいます。「異なる日付」にするには、どのように異なるかのルール(1列右に動くごとに1日加算されるとか)が必要です。

  10. マロンブック より:

    そうですか、分かりました!
    ここで紹介されているカレンダーだけでも参考になりました。
    ありがとうございました m(_ _)m

  11. のび太335 より:

    こんにちは
    初めまして

    こちらのサイトを利用して、医療費控除のe-tax用マクロを組みました。
    質問者のtukaさんと同じことがしたくてあちこち探していましたがなかなか動きませんで悩んでいました。今日、勉強を兼ねて自分でカレンダーコントロール作ったところうまく動きました。
    こちらのサイトは、とても実用的でかゆいところに手が届くようなコードがあり、大変重宝しております。また、本にも書いていないことが分かりやすく解説してあり、とても勉強になります。
    今後ともどうぞよろしくお願いいたします。

    • *you より:

      のび太335 さん、コメントありがとうございます! もったいないお言葉、とてもとても嬉しいです! お役に立てましたら光栄です~!!

  12. Jane より:

    こんばんは
    2ヶ月ほど、カレンダーを別のユーザーフォームで使うコードを探しまくりました。諦めるところで、このサイトと出会えて、ご共有のエクセルのコードを真似して、なんと思う通りに、日付がユーザーフォームのTextBoxに反映することができました!
    本当に嬉しいです。
    ご共有いただき、誠に有難うございました!

    • *you より:

      Janeさん、コメントありがとうございます。それは良かったです~~!お役に立てて私も嬉しいです!

  13. VBA学び太郎 より:

    こんばんは
    カレンダーを探しておりましたところ、こちらのサイトにたどり着きました。
    大変見やすく、素晴らしいと感じました。ありがとうございます。

    質問があるのですが、カレンダー日付けをクリックしてUserFrorm1に反映される際に、休日をクリックした時は文字が赤くなるようにするにはどうすればよろしいでしょうか。

    宜しくお願い致します。

    • *you より:

      VBA学び太郎 さん、コメントありがとうございます。フォントの色ですか~、考えたことありませんでしたが、以下のハイライト部分のように変更すればクリックしたラベルの色をそのままテキストボックスにも反映できると思います!

      ↓Module1

      Public clndr_date As Date 'テキストボックスの値を格納する変数
      Public clndr_dateColor As Long 'クリックされた日付の色
      Public clndr_flg As Boolean 'カレンダーがクリックされたか判定するフラグ
      
      Sub start()
        UserForm1.Show 'メインフォームを開く
      End Sub
      
      Sub putCalenderDate(ByVal tgtTextBox As MSForms.TextBox)
        clndr_flg = False 'フラグリセット
        If IsDate(tgtTextBox) = False Then '日付が入ってなければ
          clndr_date = Date '今日の日付を格納
        Else
          clndr_date = tgtTextBox 'テキストボックスの日付を格納
        End If
        CalenderForm.Show 'カレンダーを開く
        If clndr_flg = True Then
          tgtTextBox = Format(clndr_date, "yyyy/mm/dd") 'クリックされた日付を上書き
          tgtTextBox.ForeColor = clndr_dateColor 'フォント色を上書き
        End If
      End Sub
      

      ↓CalenderForm(記事内だとUserForm2)

      Private Sub LabelClick(ByVal i As Integer)
        If Me("Label" & i).Caption = "" Then Exit Sub 'ラベルが空だったら中止
        clndr_date = Me.ComboBox1 & "/" & Me.ComboBox2 & "/" & Me("Label" & i).Caption '日付を生成して変数に格納
        clndr_dateColor = Me("Label" & i).ForeColor 'ラベルのフォント色を変数に格納
        clndr_flg = True 'フラグを立てる
        Unload Me 'カレンダーを閉じる
      End Sub
      
  14. ふじひろ より:

    こんにちは。

    VBAのユーザーフォームのコントロールに日付を選択するコントロールが欲しくて、こちらのコードを参考にさせて頂きました。

    ありがとうございました。

    • *yuko より:

      コメントありがとうございます、お役に立てて嬉しいです!

  15. 小島 より:

    こんにちは

    マクロが初めてでこちらのコードを参考にしています。

    クリックされた日付を取得するの方法1のところで質問があります。
    日付をクリックした際にコバルトエラー「SubまたはFunctionが定義されていませんと出るのですがどうしたらよろしいですか?

    • *yuko より:

      コメントありがとうございます。おそらく、スペルミスなどの原因でプロシージャが見つかりません、という旨のコンパイルエラーではないかと思います。最初の手順から1つずつ、書く場所や文字が間違っていないかご確認ください。

  16. さちや より:

    こんにちは!
    こちらのカレンダーフォーマットが1番使いやすかったので、会社のお客様への注文書フォーマットに採用検討しています。
    カレンダーフォーマットからクリックして、セルに日付が表示されるかと思いますが、セルに表示されている日付をカレンダーフォーマットに赤い〇とかつけて反映させることなど可能なのでしょうか?
    (例:セルC1に、2024/1/12が表示されていると、こちらのカレンダーフォーマットの2024/1/12に赤い〇がつくようになる・・・といった感じです)

    ご回答よろしくお願いいたします!

    • *yuko より:

      コメントありがとうございます。こちらの記事で紹介しているカレンダーコントロールは、ユーザーフォーム上のテキストボックスに日付を読み書きする仕様ですが、コメント先は合っていますでしょうか? セルのダブルクリックで起動するカレンダーはこちらです。どちらでも、あらかじめ入力されていた日付には色が付くようになっています。どちらのコードにも、背景色を設定する.BackColor = RGB(xxx, xxx, xxx)(xxxは任意の数値)という記述がありますので、.BackColor = RGB(255, 0, 0)にすれば赤になります。

      ただ、紹介しているカレンダーはExcelのマクロがブロックされる環境だと利用できませんので、配布が目的となると適していないように思えます。そのあたりもご検討ください。


コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

コメントは承認制ですので、反映までしばらくお待ち下さい。(稀にスパムの誤判定にて届かないこともあるようですので、必要な際はお問い合わせからお願い致します。)

YouTubeでQ&Aコンテンツを企画しています

運営しているYouTubeチャンネルで、ご相談やご質問を募集しています。動画のコメントやお問い合わせページからお気軽にご相談をお寄せください。