VBAコード
EXCEL-VBA
概要
仕事でよく使う、表計算のEXCELのVBA、シート関数、簡易データベースのACCESSのVBA、SQLなどの資源となるコードを書き綴ったものです。
オリジナルカレンダー
アクセスの日付入力補助フォーム
フォームに追加された日付/時刻型のフィールドには、標準で日付選択カレンダーが表示され、カレンダーから日付をクリックするだけで日付を入力できますが、スピンボタンが月単位の送りしかできないので、過去の古い日付を入れようとすると手間がかかる2年前を入れようと思ったら、24回押さなくてはいけない。
あと、土曜日、日曜日、休日の色分けがあったほうが、やっぱり扱いやすい。
ということで、作りました。
左上のスピンボタンは、年をインクリメントします。名前は「PY」「NY]とし、間のテキストボックスは「DYEAR」としています。
右の2つのスピンボタンは、月をインクリメントをして名前は「PM」「NM]とし間のテキストボックスは「DMONTH」としています。
下の横に並んだ7つラベルは曜日を表示し、名前は「Y0」・・「Y6」とします。
その下42個のラベルは、日にちを表示し、名前は「Day0」・・「Day41」とします。
最下部のボタンは、初期はカレンダーを開いた時の日付に戻します。削除は呼び出したコントロール(編集不可の場合)の値を削除します。閉じるはフォームを閉じます。
コントロールの名前を図で表すと次のようになっています。
赤色は Label、青色はCommandButton、緑色は TextBox です。
C_Controls
'----------------------------------- 'イベントクラスをまとめる C_Controls '----------------------------------- Option Explicit '----------------------------------- 'このクラスが発生するイベント Public Event Click(myCont As Object) Public Event Change(myCont As Object) Public Event DblClick(myCont As Object, _ Cancel As Integer) Public Event MouseMove(myCont As Object, _ Button As Integer, _ Shift As Integer, _ X As Single, _ Y As Single) Public Event KeyPress(myCont As Object, _ KeyAscii As Integer) Public Event KeyDown(myCont As Object, _ KeyCode As Integer, _ Shift As Integer) 'イベントクラスのリスト Private Labels As Dictionary Private TextBoxs As Dictionary Private Buttons As Dictionary '呼び出しフォーム Private myParent As Object '----------------------------------- '呼び出しフォームを格納します。 Public Property Set Parent(ByRef Obj As Object) Set myParent = Obj End Property '----------------------------------- 'イベントクラスがあるかの確認に使います。 Public Property Get Exitst(ByVal objTypeName As String) As Boolean Select Case objTypeName Case "TextBox" Exitst = (TextBoxs.Count > 0) Case "Label" Exitst = (Labels.Count > 0) Case "CommandButton" Exitst = (Buttons.Count > 0) End Select End Property '----------------------------------- '子オブジェクトから呼び出されます。 Public Sub onClick(myCont As Object) 'イベントを発生します。 RaiseEvent Click(myCont) End Sub '----------------------------------- '子オブジェクトから呼び出されます。 Public Sub onDblClick(myCont As Object, Cancel As Integer) 'イベントを発生します。 RaiseEvent DblClick(myCont, Cancel) End Sub '----------------------------------- '子オブジェクトから呼び出されます。 Public Sub onMouseMove(myCont As Object, _ Button As Integer, _ Shift As Integer, _ X As Single, _ Y As Single) 'イベントを発生します。 RaiseEvent MouseMove(myCont, Button, Shift, X, Y) End Sub '----------------------------------- '子オブジェクトから呼び出されます。 Public Sub onChange(myCont As Object) 'イベントを発生します。 RaiseEvent Change(myCont) End Sub '----------------------------------- '子オブジェクトから呼び出されます。 Public Sub onKeyPress(myCont As Object, _ KeyAscii As Integer) 'イベントを発生します。 RaiseEvent KeyPress(myCont, KeyAscii) End Sub '----------------------------------- '子オブジェクトから呼び出されます。 Public Sub onKeyDown(myCont As Object, _ KeyCode As Integer, _ Shift As Integer) 'イベントを発生します。 RaiseEvent KeyDown(myCont, KeyCode, Shift) End Sub '----------------------------------- 'フォームから呼び出されます。 Public Sub Init() Dim Ctrl As Control Dim Obj As Object 'フォームの格納がなければ処理をしません。 If myParent Is Nothing Then Exit Sub End If 'フォームを探査してコントロールのイベントクラスを登録します。 For Each Ctrl In myParent.Controls Select Case TypeName(Ctrl) Case "TextBox" With New C_TextBox Set .Item = Ctrl Set .Parent = Me TextBoxs.Add Ctrl.Name, .Self End With Case "Label" With New C_Label Set .Item = Ctrl Set .Parent = Me Labels.Add Ctrl.Name, .Self End With Case "CommandButton" With New C_Button Set .Item = Ctrl Set .Parent = Me Buttons.Add Ctrl.Name, .Self End With End Select Next Ctrl End Sub '----------------------------------- 'イベントクラスのリストの中身を削除します。 Private Sub ObjectDelete() Dim Keys As Variant For Each Keys In TextBoxs TextBoxs(Keys).DEL Next Keys Set TextBoxs = Nothing For Each Keys In Labels Labels(Keys).DEL Next Keys Set Labels = Nothing For Each Keys In Buttons Buttons(Keys).DEL Next Keys Set Buttons = Nothing Set myParent = Nothing End Sub '----------------------------------- 'イベントクラスのリストを作成します。 Private Sub Class_Initialize() Set Labels = New Dictionary Set TextBoxs = New Dictionary Set Buttons = New Dictionary End Sub '----------------------------------- 'クラスの廃棄時、イベントクラスのリストを廃棄します。 Private Sub Class_Terminate() Call ObjectDelete End Sub
C_TextBox
'----------------------------------- 'イベントクラス C_TextBox '----------------------------------- Option Explicit '----------------------------------- Private WithEvents myTextBox As Access.TextBox Private myParent As C_Controls Private myIndex As Long '----------------------------------- ' Public Property Set Item(ByRef Obj As Access.TextBox) Set myTextBox = Obj End Property '----------------------------------- ' Public Property Get Item() As Access.TextBox Set Item = myTextBox End Property '----------------------------------- ' Public Property Set Parent(ByRef Obj As C_Controls) Set myParent = Obj End Property '----------------------------------- ' Public Property Let Index(ByVal Val As Long) myIndex = Val End Property '----------------------------------- ' Public Property Get Index() As Long Index = myIndex End Property '----------------------------------- ' Public Property Get Self() As Object Set Self = Me End Property '----------------------------------- ' Public Sub DEL() Set myTextBox = Nothing Set myParent = Nothing End Sub '----------------------------------- ' Private Sub Class_Terminate() Set myTextBox = Nothing Set myParent = Nothing End Sub '----------------------------------- ' Private Sub myTextBox_Change() Call myParent.onChange(myTextBox) End Sub '----------------------------------- ' Private Sub myTextBox_DblClick(Cancel As Integer) Call myParent.onDblClick(myTextBox, Cancel) End Sub '----------------------------------- ' Private Sub myTextBox_KeyPress(KeyAscii As Integer) Call myParent.onKeyPress(myTextBox, KeyAscii) End Sub '----------------------------------- ' Private Sub myTextBox_MouseMove(Button As Integer, _ Shift As Integer, _ X As Single, _ Y As Single) Call myParent.onMouseMove(myTextBox, Button, Shift, X, Y) End Sub
C_Label
'----------------------------------- 'イベントクラス C_Label '----------------------------------- Option Explicit '----------------------------------- Private WithEvents myLabel As Access.Label Private myParent As C_Controls Private myIndex As Long '----------------------------------- ' Public Property Set Item(ByRef Obj As Access.Label) Set myLabel = Obj End Property '----------------------------------- ' Public Property Get Item() As Access.Label Set Item = myLabel End Property '----------------------------------- ' Public Property Set Parent(ByRef Obj As C_Controls) Set myParent = Obj End Property '----------------------------------- ' Public Property Let Index(ByVal Val As Long) myIndex = Val End Property '----------------------------------- ' Public Property Get Index() As Long Index = myIndex End Property '----------------------------------- ' Public Property Get Self() As Object Set Self = Me End Property '----------------------------------- ' Public Sub DEL() Set myLabel = Nothing Set myParent = Nothing End Sub '----------------------------------- ' Private Sub Class_Terminate() Set myLabel = Nothing Set myParent = Nothing End Sub '----------------------------------- ' Private Sub myLabel_Click() Call myParent.onClick(myLabel) End Sub '----------------------------------- ' Private Sub myLabel_DblClick(Cancel As Integer) Call myParent.onDblClick(myLabel, Cancel) End Sub '----------------------------------- ' Private Sub myLabel_MouseMove(Button As Integer, _ Shift As Integer, _ X As Single, _ Y As Single) Call myParent.onMouseMove(myLabel, Button, Shift, X, Y) End Sub
C_Button
'----------------------------------- 'イベントクラス C_Button '----------------------------------- Option Explicit '----------------------------------- Private WithEvents myButton As Access.CommandButton Private myParent As C_Controls Private myIndex As Long '----------------------------------- ' Public Property Set Item(ByRef Obj As Access.CommandButton) Set myButton = Obj End Property '----------------------------------- ' Public Property Get Item() As Access.CommandButton Set Item = myButton End Property '----------------------------------- ' Public Property Set Parent(ByRef Obj As C_Controls) Set myParent = Obj End Property '----------------------------------- ' Public Property Let Index(ByVal Val As Long) myIndex = Val End Property '----------------------------------- ' Public Property Get Index() As Long Index = myIndex End Property '----------------------------------- ' Public Property Get Self() As Object Set Self = Me End Property '----------------------------------- ' Public Sub DEL() Set myButton = Nothing Set myParent = Nothing End Sub '----------------------------------- ' Private Sub Class_Terminate() Set myButton = Nothing Set myParent = Nothing End Sub '----------------------------------- ' Private Sub myButton_Click() Call myParent.onClick(myButton) End Sub '----------------------------------- ' Private Sub myButton_DblClick(Cancel As Integer) Call myParent.onDblClick(myButton, Cancel) End Sub '----------------------------------- ' Private Sub myButton_KeyDown(KeyCode As Integer, Shift As Integer) Call myParent.onKeyDown(myButton, KeyCode, Shift) End Sub '----------------------------------- ' Private Sub myButton_MouseMove(Button As Integer, _ Shift As Integer, _ X As Single, _ Y As Single) Call myParent.onMouseMove(myButton, Button, Shift, X, Y) End Sub
C_Kyuzitu
令和4年11月16日投稿「VBA で作った休日クラス」にあります。
フォーム カレンダー
'----------------------------------- 'フォーム名を カレンダー とします。 '----------------------------------- Option Compare Database Option Explicit Private WithEvents myControls As C_Controls Private GetYobi As C_KyuZitu Private Const SHIFT_MASK As Long = 1 Private Const FirstYobi As Long = vbSunday Private HoldDate As Long Private HoldYear As Long Private HoldMonth As Long Private HoldDay As Long Private Enum IncType Forward = 1 Backward = -1 End Enum Private Const IntvDay As String = "d" Private Const IntvMonth As String = "m" Private Const IntvYear As String = "yyyy" Private Const IntvWeek As String = "ww" Private CurYear As Long Private CurMonth As Long Private CurDay As Long Private DaysAndWeeks() As Long Private Yobi As Variant Private SelectDay As String Public Property Get DateNum() As Long If CurYear = 0 Then DateNum = 0 Else DateNum = DateSerial(CurYear, CurMonth, CurDay) End If End Property Public Property Get WeekNum() As Long GetYobi.SetNumDate = DateSerial(CurYear, CurMonth, CurDay) WeekNum = GetYobi.WeekNum End Property '----------------------------------- ' Private Sub Form_Load() Dim Ctrl As Access.Control Dim I As Long If Nz(Me.OpenArgs, "") = "" Then HoldDate = Date Else If IsDate(Me.OpenArgs) Then HoldDate = DateSerial(DatePart("YYYY", Me.OpenArgs), _ DatePart("m", Me.OpenArgs), _ DatePart("d", Me.OpenArgs)) Else HoldDate = Date End If End If 'カレンダーを開いたときの値を取得し、以後変更はありません。 HoldYear = Year(HoldDate) HoldMonth = Month(HoldDate) HoldDay = Day(HoldDate) Set GetYobi = New C_KyuZitu GetYobi.pYear = HoldYear CurYear = HoldYear CurMonth = HoldMonth CurDay = HoldDay Set myControls = New C_Controls With myControls Set .Parent = Me .Init End With Yobi = Array("日", "月", "火", "水", "木", "金", "土") For I = 0 To 6 Me("Y" & I).Caption = Yobi((FirstYobi - 1 + I) Mod 7) Select Case I Case 0 Me("Y" & I).ForeColor = vbRed Case 6 Me("Y" & I).ForeColor = vbBlue End Select Next I Call SetCurDisp Call DispDraw End Sub '----------------------------------- ' Private Sub Form_Close() '参照を破棄します。 Set myControls = Nothing Set GetYobi = Nothing End Sub '----------------------------------- ' Private Sub SetCurDisp() Me.DMONTH.Caption = CurMonth Me.DYEAR.Caption = CurYear End Sub '----------------------------------- ' Private Sub DispDraw() Dim Ctrl As Access.Control Dim NewSelect As String Dim OneDayYobi As Long Dim OneDaySerial As Long Dim I As Long GetYobi.pYear = CurYear '選択月の最初の日のシリアル値 OneDaySerial = DateSerial(CurYear, CurMonth, 1) '選択月の最初の日の曜日 OneDayYobi = Weekday(OneDaySerial, FirstYobi) '配列にシリアル値をセット ReDim DaysAndWeeks(0 To 41) For I = 0 To UBound(DaysAndWeeks) DaysAndWeeks(I) = OneDaySerial - (OneDayYobi - 1) + I Next I 'ラベルのキャプションに日にちをセット For I = 0 To UBound(DaysAndWeeks) Me("Day" & I).Caption = Day(DaysAndWeeks(I)) If Me.DMONTH.Caption <> Month(DaysAndWeeks(I)) Then Me("Day" & I).FontSize = 10 Else Me("Day" & I).FontSize = 12 End If If Me("Day" & I).ForeColor <> InteriorColor(DaysAndWeeks(I)) Then Me("Day" & I).ForeColor = InteriorColor(DaysAndWeeks(I)) End If If DaysAndWeeks(I) = DateSerial(CurYear, CurMonth, CurDay) Then NewSelect = "Day" & I End If Next I Call EffectDraw(NewSelect) End Sub '----------------------------------- ' Private Sub EffectDraw(NewSelect As String) If Len(SelectDay) > 0 Then If SelectDay <> NewSelect Then Me(SelectDay).SpecialEffect = acNormal End If End If SelectDay = NewSelect Me(SelectDay).SpecialEffect = acEffectSunken Me.Repaint End Sub '----------------------------------- ' Private Function InteriorColor(DrawDate As Long) As Long GetYobi.SetNumDate = DrawDate Select Case GetYobi.WeekNum Case 1, 10, 11, 12, 13 InteriorColor = vbRed Case 7 InteriorColor = vbBlue Case Else InteriorColor = vbBlack End Select End Function '----------------------------------- ' Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer) Call ShiftKeys(KeyCode, Shift) End Sub '----------------------------------- ' Private Sub myControls_Click(myCont As Object) Select Case myCont.Name Case "NM" Me.buDummy.SetFocus Call NextMonth Case "NY" Me.buDummy.SetFocus Call NextYear Case "PM" Me.buDummy.SetFocus Call PreviousMonth Case "PY" Me.buDummy.SetFocus Call PreviousYear Case "bu閉じる" Me.buDummy.SetFocus Call CloseForm(Hide:=False) Case "bu初期" Me.buDummy.SetFocus CurYear = HoldYear CurMonth = HoldMonth CurDay = HoldDay Call SetCurDisp Call DispDraw Case "bu削除" Me.buDummy.SetFocus CurYear = 0 Call CloseForm(Hide:=True) Case Else Select Case True Case InStr(myCont.Name, "Day") = 1 If myCont.SpecialEffect = acEffectSunken Then Call CloseForm(Hide:=True) Else If CurMonth <> Month(DaysAndWeeks(Mid(myCont.Name, Len("Day") + 1))) Then Call ClickDate(myCont.Name) Call SetCurDisp Call DispDraw Else Call EffectDraw(myCont.Name) Call ClickDate(myCont.Name) End If End If End Select End Select End Sub '----------------------------------- ' Private Sub myControls_DblClick(myCont As Object, Cancel As Integer) Select Case True Case InStr(myCont.Name, "Day") = 1 Call EffectDraw(myCont.Name) Call ClickDate(myCont.Name) Call CloseForm(Hide:=True) End Select End Sub '----------------------------------- ' Private Sub myControls_KeyDown(myCont As Object, KeyCode As Integer, Shift As Integer) Select Case myCont.Name Case "PM", "NM", "PY", "NY", "bu閉じる", "bu初期", "bu削除" Call ShiftKeys(KeyCode, Shift) Case Else Select Case True Case InStr(myCont.Name, "Day") = 1 Call ShiftKeys(KeyCode, Shift) End Select End Select End Sub '----------------------------------- ' Private Sub ShiftKeys(KeyCode As Integer, Shift As Integer) Dim ShiftDown As Boolean ShiftDown = ((Shift And SHIFT_MASK) > 0) Select Case KeyCode Case vbKeyEscape Call CloseForm(Hide:=False) Case vbKeyReturn Call CloseForm(Hide:=True) Case vbKeyHome If ShiftDown Then Call MoveToToday(UseCurYear:=False) Else Call MoveToToday(UseCurYear:=True) End If Case vbKeyPageUp If ShiftDown Then Call PreviousYear Else Call PreviousMonth End If Case vbKeyPageDown If ShiftDown Then Call NextYear Else Call NextMonth End If Case vbKeyRight If ShiftDown Then Call NextYear Else Call NextDay End If Case vbKeyLeft If ShiftDown Then Call PreviousYear Else Call PreviousDay End If Case vbKeyUp If ShiftDown Then Call PreviousMonth Else Call PreviousWeek End If Case vbKeyDown If ShiftDown Then Call NextMonth Else Call NextWeek End If End Select KeyCode = 0 End Sub '----------------------------------- ' Public Sub Today() Call MoveToToday(UseCurYear:=True) End Sub '----------------------------------- ' Public Sub NextDay() '1日進めます。 Call ChangeDate(IntvDay, IncType.Forward) End Sub '----------------------------------- ' Public Sub NextMonth() '1月進めます。 Call ChangeDate(IntvMonth, IncType.Forward) End Sub '----------------------------------- ' Public Sub NextYear() '1年進めます。 Call ChangeDate(IntvYear, IncType.Forward) End Sub '----------------------------------- ' Public Sub NextWeek() '1週進めます。 Call ChangeDate(IntvWeek, IncType.Forward) End Sub '----------------------------------- ' Public Sub PreviousDay() '1日戻ります。 Call ChangeDate(IntvDay, IncType.Backward) End Sub '----------------------------------- ' Public Sub PreviousMonth() '1月戻ります。 Call ChangeDate(IntvMonth, IncType.Backward) End Sub '----------------------------------- ' Public Sub PreviousYear() '1年戻ります。 Call ChangeDate(IntvYear, IncType.Backward) End Sub '----------------------------------- ' Public Sub PreviousWeek() '1週戻ります。 Call ChangeDate(IntvWeek, IncType.Backward) End Sub '----------------------------------- ' Private Sub MoveToToday(UseCurYear As Boolean) If UseCurYear Then CurYear = HoldYear End If CurMonth = HoldMonth CurDay = HoldDay Call SetCurDisp Call DispDraw End Sub '----------------------------------- ' Private Sub ChangeDate(IntvStr As String, IT As IncType) Dim bufMonth As Integer Dim bufYear As Integer Dim bufDay As Integer Dim NewSelect As String Dim OLDDate As Long Dim NewDate As Long Dim Inc As Long Dim I As Long If IT = Forward Then Inc = 1 Else Inc = -1 End If OLDDate = DateSerial(CurYear, CurMonth, CurDay) NewDate = DateAdd(IntvStr, Inc, OLDDate) bufMonth = DatePart(IntvMonth, NewDate) bufYear = DatePart(IntvYear, NewDate) bufDay = DatePart(IntvDay, NewDate) If CurMonth = bufMonth And _ CurYear = bufYear Then CurDay = bufDay For I = 0 To UBound(DaysAndWeeks) If DaysAndWeeks(I) = DateSerial(CurYear, CurMonth, CurDay) Then NewSelect = "Day" & I End If Next I Call EffectDraw(NewSelect) Else CurDay = bufDay CurMonth = bufMonth CurYear = bufYear Call SetCurDisp Call DispDraw End If End Sub '----------------------------------- ' Private Sub CloseForm(Hide As Boolean) 'サブフォームになっているときの処理 If ThisFormSub() Then Exit Sub End If If Hide Then Me.Visible = False Else DoCmd.Close acForm, Me.Name, acSaveNo End If End Sub '----------------------------------- 'サブフォームになっているかの確認 Private Function ThisFormSub() As Boolean Dim strName As String On Error Resume Next strName = Me.Parent.Name ThisFormSub = (Err.Number = 0) Err.Clear End Function '----------------------------------- ' Private Sub ClickDate(ClickName As String) Dim Num As Long Num = Mid(ClickName, Len("Day") + 1) CurYear = Year(DaysAndWeeks(Num)) CurMonth = Month(DaysAndWeeks(Num)) CurDay = Day(DaysAndWeeks(Num)) End Sub
呼び出すコントロールのCODE
Private Sub コントロール名_DblClick(Cancel As Integer) Dim UF As Access.Form 'コントロールの値を初期表示するようにしています。 DoCmd.OpenForm "カレンダー", WindowMode:=acDialog, OpenArgs:=Me.コントロール名.Value For Each UF In Forms If UF.Name = "カレンダー" Then If UF.DateNum = 0 Then '削除ボタンが押されたので、コントロールを空にします。 Me.コントロール名.Value = "" Else '和暦で値をセットします。 Me.コントロール名.Value = Format(UF.DateNum, "ggge年m月d日") End If '値を取得したので開いているフォームを閉じます。 DoCmd.Close acForm, "カレンダー", acSaveNo End If Next UF End Sub