VBAからGDI+を使う資料集
inet上の達人の方々から御教示いただいたコードを少しアレンジさせてもらっています(切り貼りとも言います)。
  1. ホーム
  2. etc
  3. Simple calender


Simple calender

シンプルなカレンダーのフォームを表示して、日付を選択入力できるツールです。
土日に色を付けて判別できる様にする機能しかありません。
コントロールを配列化していますが、「疑似からの脱却」を行う事で、使いやすくしています。 日付を表示するlabelのクラスからは呼び元の関数を実行できるだけ(With Eventでクラスの配列を使えない)なので、 いまいち使いにくかったのですが、参考サイトの考え方の導入で呼び元のコードがすっきりします。親クラスをUserFormにするのに 少々悩みました。
http://www.h3.dion.ne.jp/~sakatsu/Breakthrough_P-Ctrl_Arrays10.htm
本家に立派なカレンダーが載っていますが、そちらとは無関係で、触ってみておりません。
作業用ワークシートつかいまくりの、数年前に作成したものを手直して作業用シート不要にしたものです。


☆Sheet Moduleに記述
'====================================================================

'simpleCalenderFormはUserFormの名前です
Private WithEvents myform As simpleCalenderForm

Private Sub myform_dateSelect(selectedDate As Date)
  ActiveCell.Value = selectedDate
  Unload myform
End Sub

Private Sub Worksheet_SelectionChange(ByVal Target As Range)
  If Target.Cells.Count > 1 Then Exit Sub
  If Not Intersect(Target, Me.Columns(1)) Is Nothing Then
    simpleCalenderForm.Show vbModeless
    Set myform = simpleCalenderForm
  End If
End Sub

☆simpleCalenderFormのForm Moduleに記述
'====================================================================
'UserFormには、ComboBox2個を設置しておきます。Labelは動的に設置します。

Public Event dateSelect(selectedDate As Date)

Dim myClsIndex As Integer               'ラベルコントロールの番号
Dim labelArray() As LabelCtrl           'ラベルコントロールの配列
Dim currentMonth As Long
Dim currentYear As Long
'Dim myCaller As Object
Dim myDate As Date

'カレンダーフォームのための定数
Const yOffset As Integer = 16           'フォーム上の、コンボボックス分のoffset
Const labelWidth As Integer = 12
Const labelHeight As Integer = 12
Const dataNum As Integer = 31           '31日/月
Const weekdays As Integer = 7

Private Sub UserForm_Initialize()
    Dim i As Integer
    Dim myMonth As Integer
    Dim myYear As Integer
    Dim etchingLines() As String
    
    myClsIndex = 0
    'ユーザーフォームの設定
    With Me
        .caption = "日付選択"
        .Width = (labelWidth + 2) * weekdays + 10
        .Height = (labelHeight + 2) * 7 + yOffset + 2
        .BackColor = RGB(255, 255, 255)
    End With
    
    'コンボボックスの設定
    myYear = year(Date)
    '当日の年を設定  過去2年分まで選択可
    currentYear = myYear
    With Me.ComboYear
        .AddItem Format(myYear, "####")
        .AddItem Format(myYear - 1, "####")
        .AddItem Format(myYear - 2, "####")
        .Value = Format(myYear, "####")
    End With
    '当日の月を設定
    currentMonth = Month(Date)
    With Me.ComboMonth
        For myMonth = 1 To 12
            .AddItem Format(myMonth, "#")
        Next myMonth
        .Value = Format(Month(Date), "#")
    End With
    'ラベルコントロールの配列生成
    For i = 1 To dataNum
        Call addLabel(Format(i, "#"))
    Next i
    'ラベルコントロールの配列の初期設定
    Call setLabels
    kFormPosCell Me
End Sub

'ラベルコントロール配列のクリックイベントで起動されるルーチン
Public Sub labelClicked(clickItemNo As Integer)
  myDate = DateValue(CStr(currentYear) & "/" & CStr(currentMonth) & "/" & CStr(clickItemNo))
  RaiseEvent dateSelect(myDate)
'  Me.Hide
End Sub

'ラベルコントロール配列のプロパティ設定
Sub setLabels()
    Dim i As Integer
    'Userform上のラベルの列・行を指している
    Dim columnNo As Long
    Dim rowNo As Long
    Dim dateTable As Range
    Dim labelTop As Long
    Dim labelLeft As Long
    Dim textColor As String
    Dim strDate As String

    '日付、曜日等の入った範囲を指定
    columnNo = 1
    rowNo = 1
    For i = 1 To dataNum
        '日付関数のエラーを利用することで、カレンダー上
        'あり得ない日を表示しない様にしている
        strDate = CStr(currentYear) & "/" & CStr(currentMonth) & "/" & CStr(i)
        If dateCheck(strDate) Then
            columnNo = Weekday(DateValue(strDate))
            labelLeft = 3 + (columnNo - 1) * (labelWidth + 2)
            labelTop = yOffset + (rowNo - 1) * (labelHeight + 2)
            Select Case columnNo
                Case 1
                    textColor = "Red"
                Case 7
                    textColor = "Blue"
                Case Else
                    textColor = "Black"     '実際は使っていない
            End Select
            With labelArray(i)
                .left = labelLeft
                .top = labelTop
                .visible = True
                .textColor = textColor
            End With
            '土曜日の次は行を改める
            If columnNo = 7 Then rowNo = rowNo + 1
        Else
            labelArray(i).visible = False
        End If
    Next i
End Sub

'ラベルの追加
Private Function addLabel(caption As String) As Integer
    Dim myLabel As MSForms.Label
    
    Set myLabel = Me.Controls.Add("Forms.Label.1", , False)
    myClsIndex = myClsIndex + 1
    With myLabel
        .caption = caption
        .Height = labelHeight
        .Width = labelWidth
        .BackColor = RGB(255, 255, 255)
        .TextAlign = fmTextAlignRight
    End With
    ReDim Preserve labelArray(1 To myClsIndex)
    Set labelArray(myClsIndex) = New LabelCtrl
    Set labelArray(myClsIndex).parent = Me
    labelArray(myClsIndex).S_SetLabel myLabel, myClsIndex
End Function

Private Sub ComboMonth_Change()
    currentMonth = ComboMonth.Value
    'この辺は過去の名残で必要性?
    If Me.visible Then
        'ラベルの再設定
        Call setLabels
    End If
End Sub

Private Sub ComboYear_Change()
    currentYear = ComboYear.Value
    If Me.visible Then
        Call setLabels
    End If
End Sub

'カレンダー上正しい日付かチェックする関数
Private Function dateCheck(strDate As String) As Boolean
  Dim dummyDate As Date
  
  On Error GoTo errHandle
  dummyDate = DateValue(strDate)
errHandle:
  If Err.Number <> 0 Then
    dateCheck = False
  Else
    dateCheck = True
  End If
End Function

'kFormPosCell関数
'http://www2.aqua-r.tepm.jp/~kmado/ke_m11.htm
'ユーザーフォームを指定セルに表示
'uf:ユーザーフォーム
'pos:表示するセル位置(既定値はActiveCellの右下)
Sub kFormPosCell(uf As Object, Optional pos As Range)
'中味は参考サイトをご参照下さい。
End Sub

☆Class Module: LabelCtrl
'====================================================================
Private WithEvents myLabel As MSForms.Label
Private myIndex As Integer
Private myParent As Object 'simpleCalenderForm

Public Sub S_SetLabel(newLabel As MSForms.Label, index As Integer)
    Set myLabel = newLabel
    myIndex = index
End Sub

Private Sub myLabel_Click()
    Call Me.parent.labelClicked(myIndex)
End Sub

Public Property Get top() As Integer
    top = myLabel.top
End Property

Public Property Let top(myNewTop As Integer)
    myLabel.top = myNewTop
End Property

Public Property Get left() As Integer
    left = myLabel.left
End Property

Public Property Let left(myNewleft As Integer)
    myLabel.left = myNewleft
End Property

Public Property Let visible(myNewVisible As Boolean)
    If myNewVisible Then
        myLabel.visible = True
    Else
        myLabel.visible = False
    End If
End Property

Public Property Let textColor(myNewTextColor As String)
    With myLabel
        Select Case myNewTextColor
            Case "Red"
                .ForeColor = &HFF&
            Case "Blue"
                .ForeColor = &HFF0000
            Case Else
                .ForeColor = &H0&
        End Select
    End With
End Property

Public Property Get parent() As Object 'simpleCalenderForm
    Set parent = myParent
End Property

Public Property Set parent(newParent As Object)
    Set myParent = newParent
End Property