質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

Q&A

解決済

1回答

4105閲覧

VBA:作成したカレンダーフォームを複数のフォームで使用したい

kolobokkule

総合スコア19

VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

0グッド

0クリップ

投稿2019/03/06 14:47

編集2019/03/06 14:49

前提・実現したいこと

ユーザーフォームとクラスモジュールを使用して、フォーム上で使用できるカレンダーを作成しました。
カレンダーの日付をクリックすると、指定したテキストボックスにその日付が記入できる仕組みです。
一つのフォームでの呼び出しでは問題なく動作するのですが、その実行コードを残したまま、他のフォームにてカレンダーを起動するとエラーが出てしまいます。
カレンダーの呼び出しを行うフォームが3つあるので、3か所どこから呼び出しても正常に動作してくれるのが理想です。

このエラーが発生する原因と対処法を教えていただきたいです。
よろしくお願いいたします。

発生している問題・エラーメッセージ

実行時エラー'-2147024809(80070057) 指定されたオブジェクトは見つかりません。

カレンダーフォーム(frmCalendar)のコード

コントロールは、コンボボックス2つ(cmbYear, cmbMonth)、スピンボタン1つ(spinbutton1)です。

VBA

1 2Option Explicit 3 4Private arrayClass() As New clsCalendar 5Public entryControl As String 6Public ParentForm As UserForm 7 8Private Function CheckParam() As Boolean 9 'コンボボックスの値が指定されていなければ、False 10 If cmbYear.Value = "" Then 11 CheckParam = False 12 Exit Function 13 End If 14 If cmbMonth.Value = "" Then 15 CheckParam = False 16 Exit Function 17 End If 18 CheckParam = True 19End Function 20 21Private Sub cmbMonth_Change() 22 If CheckParam() Then 23 makeCalendar cmbYear.Value, cmbMonth.Value 24 End If 25End Sub 26 27Private Sub cmbYear_Change() 28 If CheckParam() Then 29 makeCalendar cmbYear.Value, cmbMonth.Value 30 End If 31End Sub 32 33Private Sub SpinButton1_Change() 34 35End Sub 36 37Private Sub SpinButton1_SpinDown() 38 If cmbMonth.Value = 1 Then 39 cmbMonth.Value = 12 40 cmbYear.Value = cmbYear.Value - 1 41 Else 42 cmbMonth.Value = cmbMonth.Value - 1 43 End If 44End Sub 45 46Private Sub SpinButton1_SpinUp() 47 If cmbMonth.Value = 12 Then 48 cmbMonth.Value = 1 49 cmbYear.Value = cmbYear.Value + 1 50 Else 51 cmbMonth.Value = cmbMonth.Value + 1 52 End If 53End Sub 54 55 56Private Sub UserForm_Initialize() 57 Dim lbl As MSForms.Label 58 Dim i As Integer, j As Integer, c As Integer 59 Dim weekdays() As Variant 60 '曜日の設定 61 weekdays() = Array("日", "月", "火", "水", "木", "金", "土") 62 For i = 1 To 7 63 Set lbl = Me.Controls.Add("Forms.Label.1", , True) 64 With lbl 65 .Caption = weekdays(i - 1) 66 .Width = 15 67 .Height = 15 68 .Left = 5 + (.Width + 2) * (i - 1) 69 .Top = 20 70 .BorderColor = &H666666 71 .BorderStyle = fmBorderStyleSingle 72 .Font.Size = 11 73 End With 74 Next 75 c = 1 76 For i = 1 To 6 '縦方向 77 For j = 1 To 7 '横方向 78 Set lbl = Me.Controls.Add("Forms.Label.1", , True) 79 With lbl 80 .Width = 15 81 .Height = 15 82 .Left = 5 + (j - 1) * (.Width + 2) 83 .Top = 20 + i * (.Height + 2) 84 .BorderColor = &H666666 85 .BorderStyle = fmBorderStyleSingle 86 .Font.Size = 11 87 .TextAlign = fmTextAlignRight 88 .Name = "D" & c 89 ReDim Preserve arrayClass(c) 90 arrayClass(c).NewCalendar lbl, Me 91 c = c + 1 92 End With 93 Next 94 Next 95 '3年前から3年後までの西暦をコンボボックスに追加 96 For i = Year(Date) - 3 To Year(Date) + 3 97 cmbYear.AddItem i 98 Next 99 '月をコンボボックスに追加 100 For i = 1 To 12 101 cmbMonth.AddItem i 102 Next 103 'コンボボックスに値を代入すると、Changeイベントが発生する 104 '現在の西暦と月を表示しておく 105 cmbYear.Value = Year(Date) 106 cmbMonth.Value = Month(Date) 107 'Public変数に呼び出し元のデータを格納 108 Set ParentForm = frmCRecordSearch 109End Sub 110 111Sub makeCalendar(Nen As Integer, Tsuki As Integer) 112 Dim i As Integer, c As Integer 113 Dim Weekday_of_firstday As Integer 114 Dim LastDay As Integer 115 Dim MyDate As Date 116 'ラベルの表示をクリア 117 For i = 1 To 42 118 Controls("D" & i).Caption = "" 119 Next 120 c = 1 121 'その日の曜日を数値として取得 122 Weekday_of_firstday = Getweekday1st(Nen, Tsuki) 123 LastDay = Day(DateSerial(Nen, Tsuki + 1, 1) - 1) - 1 124 For i = Weekday_of_firstday To Weekday_of_firstday + LastDay 125 Controls("D" & i).Caption = c 126 Controls("D" & i).Tag = CStr(DateSerial(Nen, Tsuki, c)) 127 Controls("D" & i).ForeColor = vbBlack 128 c = c + 1 129 Next 130 '前月の日付の設定 131 MyDate = DateSerial(Nen, Tsuki, 1) - 1 132 c = Day(MyDate) 133 For i = Weekday_of_firstday - 1 To 1 Step -1 134 Controls("D" & i).Caption = c 135 Controls("D" & i).Tag = CStr(DateSerial(Year(MyDate), Month(MyDate), c)) 136 Controls("D" & i).ForeColor = RGB(100, 100, 100) 137 c = c - 1 138 Next 139 '来月の日付の設定 140 MyDate = DateSerial(Nen, Tsuki + 1, 1) 141 c = 1 142 For i = Weekday_of_firstday + LastDay + 1 To 42 143 Controls("D" & i).Caption = c 144 Controls("D" & i).Tag = CStr(DateSerial(Year(MyDate), Month(MyDate), c)) 145 Controls("D" & i).ForeColor = RGB(100, 100, 100) 146 c = c + 1 147 Next 148End Sub 149 150Private Function Getweekday1st(Nen As Integer, Tsuki As Integer) As Integer 151 '引数の年と月に関して、1日の曜日を数字で取得(日が1、月が2・・・) 152 Dim firstDay As Date 153 firstDay = DateSerial(Nen, Tsuki, 1) 154 Getweekday1st = Weekday(firstDay, vbSunday) 155End Function 156

カレンダーのクラスモジュール(clsCalendar)

VBA

1Option Explicit 2 3Private WithEvents myLbl As MSForms.Label 4Private mForm As UserForm 5 6Public Sub NewCalendar(lbl As MSForms.Label, objForm As UserForm) 7 'オブジェクト指向言語ではコントラクタの役割 8 Set myLbl = lbl 9 Set mForm = objForm 10End Sub 11 12Private Sub myLbl_Click() 13 'myLblのTagプロパティには、日付が代入されている 14 frmCalendar.ParentForm.Controls(frmCalendar.entryControl).Text = myLbl.Tag 15 Unload mForm 16End Sub 17

カレンダーの呼び出しコード

コントロールには日付を入れるテキストボックス(yxyDate1, txtDate2)、カレンダーを表示するボタン(btnDispCalendar1, btnDispCalendar2)です。この呼び出しコードを2つ以上のフォームで行おうとするとエラーが発生してしまいます。

VBA

1Private Sub btnDispCalendar1_Click() 2 frmCalendar.entryControl = "txtDate1" 3 frmCalendar.Show 4End Sub 5 6Private Sub btnDispCalendar2_Click() 7 frmCalendar.entryControl = "txtDate2" 8 frmCalendar.Show 9End Sub

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

imihito

2019/03/06 22:58

ParentForm が既定の frmCRecordSearch 固定のようですが、それは意図した動作ですか?(そもそもの動作として、コントロールの名前では無く、オブジェクトそのものを渡した方がスマートな気もします)
kolobokkule

2019/03/07 03:59 編集

すみません、そこの補足を忘れていました。 frmCRecordSearchの部分は、フォームによって変更しています。 オブジェクトで渡した結果、うまく動作するという可能性はありますでしょうか?
guest

回答1

0

ベストアンサー

カレンダーの呼び出しを行うフォームが3つあるので、3か所どこから呼び出しても正常に動作してくれるのが理想です。

imihitoさんからも指摘がありますが、
Set ParentForm = frmCRecordSearchと対象フォームを固定してしまったら、
呼び出すフォーム毎にカレンダーフォームを作成する必要がありますので、無駄ですよね。

さらに entryControl をオブジェクトとして渡せば、ParentForm変数自体不要です。

クラスモジュール等複雑になっているので、シンプルなモデルでのサンプルコードを提示しておきます。
カレンダーフォームにラベル1つを生成して、それをクリックするとCaptionを対象コントローに代入。

カレンダーフォーム(frmCalendar)のコード

vba

1Public entryControl As MSForms.TextBox 2Private WithEvents myLbl As MSForms.Label 3 4Private Sub myLbl_Click() 5 entryControl.Value = myLbl.Caption 6 Unload Me 7End Sub 8 9Private Sub UserForm_Initialize() 10 Set myLbl = Me.Controls.Add("Forms.Label.1", , True) 11 With myLbl 12 .Caption = Format(Date, "yyyy/mm/dd") 13 .Width = 100 14 .Height = 15 15 .Left = 20 16 .Top = 20 17 .BorderColor = &H666666 18 .BorderStyle = fmBorderStyleSingle 19 .Font.Size = 11 20 End With 21End Sub

カレンダーの呼び出しコード

vba

1Private Sub btnDispCalendar1_Click() 2 Set frmCalendar.entryControl = Me.TextDate1 3 frmCalendar.Show 4End Sub

このように MSForms.TextBox として渡せば、どのフォームから呼び出してもそのテキストボックスに代入されます。また、ワークシート上のテキストボックスでも問題なく代入できます。

別案

自分はこのような時、ユーザーフォームの Property を使って、関数のように使える設計にすることがあります。

カレンダーフォーム(frmCalendar2)のコード

vba

1Private ckDate As Date 2Private WithEvents myLbl As MSForms.Label 3 4Public Property Get GetDate() As Date 5 Me.Show 6 GetDate = CDate(ckDate) 7 Unload Me 8End Property 9 10Private Sub myLbl_Click() 11 ckDate = myLbl.Caption 12 Me.Hide 13End Sub 14 15Private Sub UserForm_Initialize() 16 Set myLbl = Me.Controls.Add("Forms.Label.1", , True) 17 With myLbl 18 .Caption = Format(Date, "yyyy/mm/dd") 19 .Width = 100 20 .Height = 15 21 .Left = 20 22 .Top = 20 23 .BorderColor = &H666666 24 .BorderStyle = fmBorderStyleSingle 25 .Font.Size = 11 26 End With 27End Sub

カレンダーの呼び出しコード

vba

1Private Sub btnDispCalendar2_Click() 2 Me.TextDate2.Value = frmCalendar2.GetDate() 3End Sub

呼び出しが1行ですみますし、代入先は、テキストボックスでもラベルでもシートのセルでもなんでもOKになります。

自分のブログでこのコードの動作原理を解説したトピックを書きましたので参考にしてください。

ユーザーフォームで入力ダイアログを作成する - hatena chips

投稿2019/03/07 05:31

編集2019/03/12 14:06
hatena19

総合スコア33715

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kolobokkule

2019/03/08 02:21

回答ありがとうございます。 解答を参考にクラスモジュールの記述を ``` Private Sub myLbl_Click() 'myLblのTagプロパティには、日付が代入されている frmCalendar.entryControl.Value = myLbl.Tag Unload mForm End Sub ``` と書き換え、呼び出しのコードを ``` Private Sub btnDispCalendar1_Click() Set frmCalendar.entryControl = Me.txtDate1 frmCalendar.Show End Sub ``` としたところ、理想通りに動作してくれました。 frmCalendar2のほうも試してみたいと思います。 ありがとうございました。
kolobokkule

2019/03/13 07:41

追記ありがとうございます。 ブログのほうも参考にさせていただきます。 まだまだ初心者なので、とても助かりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問