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

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

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

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

Q&A

解決済

2回答

2211閲覧

クラスモジュールで関連付けたチェックボックスをCollectionオブジェクトに格納したい

halmichi

総合スコア12

VBA

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

0グッド

0クリップ

投稿2020/09/15 11:29

編集2020/09/17 07:20

前提・実現したいこと

任意の数だけ自動生成されたOLEオブジェクトのチェックボックスを連動させるためにクラスモジュールを使いたい
の続きでまた詰まってしまいました。
初めの状態

1行目の日付とA列の名前の分だけチェックボックスを生成し、セルに入れます。
チェックボックスは1行2列が一単位なので、日付の後ろの列を増やしてチェックボックスを入れていきます。
左のチェックボックスがオンになると右のチェックボックスが入力可能になり、左が外れると右のチェックボックスはオフになったうえで入力不可にします。

これをクラスモジュールでチェックボックスの分だけ関連付けてCollectionオブジェクトに格納したいのですが、エラーになってしまいます。

チェックボックスをユーザーフォームに生成することも考えたのですが、パソコンをいじるのになれていない人も入力する予定で、表のほうがわかりやすいとの意見があったのでこの形で行きたいです。

よろしくお願いいたします。

追記分
全体像

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

修飾子が不正ですと出ます。 下から6行目のChkesが反転します。

該当のソースコード

VBA

1Option Explicit 2 3Public chBc As Long 4Private Chkes() As New clsLinkedCheckBoxes'引数に1行上のchBcを入れた 1 To chBcにするとエラーが出るので消しました。定数式が必要ですと出ます。 5Dim myChbes As Collection 6 7 8Private Sub CommandButton1_Click()'日付入力 ここは問題ないです。 9InputYearMonth: 'A1セルに年月を入力させる。日付の形でなければループさせる 10Dim ym As String 11Dim flg As Boolean 12 13 flg = False 14 Do 15 16 ym = InputBox(Prompt:="年月を入力してください", Title:="年月入力") 17 If Len(ym) = 0 Then Exit Sub 18 If IsDate(ym) Then flg = True 19 Loop Until flg = True 20 21Cells(1, 1).Value = ym 22 23InputDays: 24Dim ds As String 25Dim dates As Date 26Dim i As Long 27 28 For i = 1 To 4 29 Do 30 flg = False 31 ds = InputBox(Prompt:=i & "日目の日付を入れてください", Title:="日付入力") 32 If Len(ds) = 0 Then 33 If i = 1 Then 34 Exit Sub 35 Else 36 Exit For 37 End If 38 ElseIf Len(ds) <> 0 Then 39 dates = DateAdd("d", ds - 1, Cells(1, 1).Value) 40 End If 41 If Month(dates) = Month(CDate(ym)) Then flg = True 42 'Debug.Print Month(dates) 43 'Debug.Print Month(CDate(ym)) 44 If flg = False Then MsgBox "月は変えられません", vbExclamation 45 Loop Until flg = True 46 47 Range(Cells(1, i + 1), Cells(2, i + 1)).Value = dates 48 Cells(1, i + 1).NumberFormatLocal = "d" 49 Cells(2, i + 1).NumberFormatLocal = "aaaa" 50 Next i 51 52Inputpts: '名を入力していく キャンセルか入力なしで終わりか聞く 53Dim pts As String 54Dim Result As Long 55Dim k As Integer 56Dim flg2 As Boolean 57Dim dc As Integer 58 59 k = 5 60 pts = 0 61 flg2 = False 62 63Do 64 Do 65 pts = InputBox(Prompt:="名を入れてください", Title:="名前入力") 66 If Len(pts) <> 0 Then 67 Cells(k, 1).Value = pts 68 k = k + 1 69 End If 70 Loop Until Len(pts) = 0 71 72 Result = MsgBox("入力を終了しますか?", vbYesNo + vbExclamation) 73 If Result = vbYes Then 74 flg2 = True 75 End If 76 77Loop Until flg2 = True 78 79Dim apc As Integer 80apc = k - 5 81 If apc = 1 Then 82 With Range("A4") 83 .Interior.Color = RGB(255, 0, 0) 84 .Font.Color = RGB(255, 255, 255) 85 .Value = apc 86 End With 87 ElseIf apc > 9 Then 88 With Range("A4") 89 .Interior.Color = RGB(0, 255, 255) 90 .Value = apc 91 End With 92 Else 93 Range("A4").Value = apc 94 End If 95 96'パブリックのchBcにチェックボックスの数を格納 97dc = Range("A1").End(xlToRight).Column - 2 98chBc = apc * dc 99 100crDdmChb: 'ドロップダウンメニューを生成 101For i = 2 To 5 102 With Cells(3, i).Validation 103 .Delete 104 .Add Type:=xlValidateList, AlertStyle:=xlValidAlertStop, Formula1:="泉,北" 105 End With 106 Cells(3, i).Interior.Color = RGB(255, 255, 204) 107Next i 108 109End Sub 110Private Sub CommandButton2_Click()'後ろから列を挿入すればいいのに力技でレンジに格納する文字列を作ってしまっています。 111Dim apc As Integer 112Dim dc As Integer 113Dim i As Integer 114Dim j As Integer 115Dim pc As Integer 116Dim rg As String 117Dim rgs As String 118Dim edc As String 119Dim edcs As String 120Dim edcs2 As String 121Dim cn As Variant 122Dim chbr As String 123Dim checkBoxCells As Range 124Dim checkBoxCell As Range 125 126'枠組み作製 127dc = Range("A1").End(xlToRight).Column '最終入力列を取得 128 For i = 2 To dc '数字に対応したアルファベットを作ってRange()に格納する範囲を作成 129 rg = Chr(i + 65) 130 edc = Chr((i * 2 - 1) + 64) 131 rgs = rgs + rg + ":" + rg + "," 132 edcs = edcs + edc + ":" + edc + "," 133 edcs2 = edcs2 + edc + "," 134 135 Next i 136rgs = Left(rgs, Len(rgs) - 1) 137edcs = Left(edcs, Len(edcs) - 1) 138edcs2 = Left(edcs2, Len(edcs2) - 1) 139 140 141Range("" + rgs + "").Insert '飛び飛び列挿入 142Range("" + edcs + "").Validation.Delete 143Range("" + edcs + "").ClearFormats 144 145'指定した範囲にチェックボックス生成 146pc = Range("A5").End(xlDown).Row 147cn = Split(edcs2, ",") 'カンマで区切って文字列にする 148Debug.Print VBA.TypeName(cn(0)) 149 For j = 0 To dc - 2 150 chbr = chbr + cn(j) + "5" + ":" + cn(j) + CStr(pc) + "," 151Next j 152 153chbr = Left(chbr, Len(chbr) - 1) 154Set checkBoxCells = Range("" + chbr + "") 155checkBoxCells.Select 156 157 For Each checkBoxCell In checkBoxCells 158 Sample_CheckBox1 checkBoxCell 159 Next 160 161 Set checkBoxCells = Range("" + chbr + "").Offset(0, -1) 162 163 For Each checkBoxCell In checkBoxCells 164 Sample_CheckBox2 checkBoxCell 165 Next 166 167 Call clsLinked 168 169End Sub 170 171Sub Sample_CheckBox2(ByVal checkBoxCell As Range) 172'チェックボックスのみとしてセルの中央に配置する 173 174 Application.ScreenUpdating = False 175 176 With Me.OLEObjects.Add(ClassType:="Forms.CheckBox.1", _ 177 Link:=False, DisplayAsIcon:=False) 178 .Width = ActiveCell.Width - 5 179 .Height = ActiveCell.Height - 5 180 '対象セルの中央配置 181 .Top = checkBoxCell.Top + (checkBoxCell.Height - .Height) / 2 182 .Left = checkBoxCell.Left + (checkBoxCell.Width - .Width) / 2 183 With .Object 184 'テキスト 185 .Caption = "" 186 '背景色 187 .BackColor = rgbWhite '色定数:白 188 'チェックボックスON=True,OFF=False 189 .Value = False 190 End With 191 End With 192 193 Application.ScreenUpdating = True 194 195End Sub 196 197Sub Sample_CheckBox1(ByVal checkBoxCell As Range) 198'チェックボックスのみとしてセルの中央に配置する 199 200 Application.ScreenUpdating = False 201 202 With Me.OLEObjects.Add(ClassType:="Forms.CheckBox.1", _ 203 Link:=False, DisplayAsIcon:=False) 204 '対象セルの枠内に収める 205 .Width = checkBoxCell.Width - 5 206 .Height = checkBoxCell.Height - 5 207 .Top = checkBoxCell.Top + (checkBoxCell.Height - .Height) / 2 208 .Left = checkBoxCell.Left + (checkBoxCell.Width - .Width) / 2 209 With .Object 210 'テキスト 211 .Caption = "リハ" 212 '文字色 213 .ForeColor = rgbBlack '色定数:haiiro H00C0C0C0 214 'フォントサイズ 215 .Font.Size = 9 216 '背景色 217 .BackColor = rgbWhite '色定数:青色 218 'チェックボックスON=True,OFF=False 219 .Value = False 220 .Enabled = False 221 End With 222 End With 223 224 Application.ScreenUpdating = True 225 226End Sub 227Private Sub clsLinked() 228 Dim i As Long 229 230 Set myChbes = New Collection 231 232 With Worksheets("Sheet1") 'チェックボックスのあるシート 233 For i = 1 To chBc 234 Chkes().SetCtrl .OLEObjects("CheckBox" & i + chBc).Object, _'先頭のChkesが修飾子が不正とのエラーが出ます。 235 .OLEObjects("CheckBox" & i).Object 236 End With 237 myChbes.Add Chkes() 238 Next 239End Sub

class

1Private WithEvents Chk1 As MSForms.CheckBox 2Private Chk2 As MSForms.CheckBox 3 4Public Sub SetCtrl(newChk1 As MSForms.CheckBox, newChk2 As MSForms.CheckBox) 5 Set Chk1 = newChk1 6 Set Chk2 = newChk2 7End Sub 8 9Private Sub Chk1_Click() 10 If Chk1.Value = False Then 11 Chk2.Value = False 12 Chk2.Enabled = False 13 Else 14 Chk2.Enabled = True 15 End If 16End Sub 17 18

試したこと

エクセルVBAでクラスのインスタンス生成時に初期データを格納するメソッドを作る方法
リンクの一連の記事でクラスモジュールとCollectionオブジェクトを勉強しました。

VBA

1Option Explicit 2 3Public chBc As Long 4Private Chkes() As clsLinkedCheckBoxes'動的配列を宣言 5 6'コマンドボタン1の中 A3にチェックボックスの数を格納 7dc = Range("A1").End(xlToRight).Column - 2 8chBc = apc * dc 9Range("A3").Value = chBc 10 11Private Sub clsLinked() 12 Dim i As Long 13 Dim Chkes As clsLinkedCheckBoxes 'ここでクラス変数を宣言、生成はしない 14 15 16 chBc = Range("A3").Value 17 With Me 'チェックボックスのあるシート 18 For i = 1 To chBc 19 Set Chkes = New clsLinkedCheckBoxes 'ここでクラスの実体を生成してクラス変数に格納 20 Chkes.SetCtrl .OLEObjects("CheckBox" & i + chBc).Object, _ 21 .OLEObjects("CheckBox" & i).Object 22 23 Next 24 End With 25End Sub 26

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

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

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

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

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

hatena19

2020/09/15 14:35

回答の前に確認ですが、CommandButton1、CommandButton2 はどこに配置してますか。 画像のシート(チェックボックスのあるシート)上ですか。 また、前のコード(クラスでない方)はこのシートのモジュールに記述してますか。
halmichi

2020/09/15 22:11

ありがとうございます。 画像のシートに配置しています。Sheet1に記載できていると思います。 画像を追加しました。 よろしくお願いいたします。
sinzo

2020/09/16 00:44

クラスのオブジェクト名がclsLinkedCheckBoxesで Private Chkes As New clsLinkedCheckBoxes と With Worksheets("Sheet1") For i = 1 To chBc Chkes.SetCtrl .OLEObjects("CheckBox" & i + chBc).Object, _ .OLEObjects("CheckBox" & i).Object myChbes.Add Chkes() Next End With でエラーは消えるけど、、、?
halmichi

2020/09/16 06:55

ありがとうございます。 エラーは出なくなりましたが、関連付けはできないようです。 デバッグ用にchBcを指定したまま進めると、Collectionオブジェクトに格納する行で オブジェクトがプロパティかメソッドをサポートしていないというエラーが出てしまいます。
sinzou

2020/09/16 08:48 編集

PCちがうのでこちらから回答します。 私のWin10、office365、64bitでは エラーなく終了しますが、動きませんね hatena19さんのに書き換えても同じく(chBc=5)動きません クラスについては勉強中ですが Collectionに収めて、、、保持する必要は?です EndSubの前のデバッグでは想定内なのですよね。 chBcはCommandButton1で計算されているのですね
halmichi

2020/09/17 07:02

ありがとうございます。 こちらの環境も同じです。 >chBcはCommandButton1で計算されているのですね その通りです。
guest

回答2

0

クラスについては勉強中なので

セル範囲の作成について

VBA

1'枠組み作製 2dc = Range("A1").End(xlToRight).Column '最終入力列を取得 3 4For i = 2 To dc '作業列(Range()に格納する文字列)を作成 5edcs = edcs & Columns((i - 2) * 2 + 3).Address(False, False) & "," 6rgs = rgs & Columns(i + 1).Address(False, False) & "," 7Next 8rgs = Left(rgs, Len(rgs) - 1) 9edcs = Left(edcs, Len(edcs) - 1) 10Debug.Print rgs 11Debug.Print edcs 12 13 14pc = Range("A5").End(xlDown).Row 15Set checkBoxCells = Intersect(Rows("5:" & pc), Range(edcs)) 16Debug.Print checkBoxCells.Address(False, False) 17 18Stop

こんなこともできます、参考まで

投稿2020/09/16 09:45

sinzou

総合スコア392

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

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

halmichi

2020/09/17 07:04

列のみを指定して相対参照を指定するとこの文字列が取り出せるんですね。 勉強になりました。
guest

0

ベストアンサー

まず、冒頭のこの部分

vba

1Public chBc As Long 2Private Chkes() As New clsLinkedCheckBoxes 3Dim myChbes As Collection

Chkes() とすると動的配列になり、宣言時点では要素数0なので、 New clsLinkedCheckBoxes と生成(New)しても、ないもの対して生成はできない。
Private Chkes(44) As New clsLinkedCheckBoxes
というように定数を設定すれば定数分のクラスを生成(New)できる。
クラス数が固定ならこの方法がお手軽ではある。

Collection は自由に追加・削除できるので、クラス数が固定でない場合は、これに格納するとよい。
Collection に格納するなら、配列(Chkes() )は不要。

そして、clsLinked()は下記のように修正してください。

vba

1Private Sub clsLinked() 2 Dim i As Long 3 Dim Chk As clsLinkedCheckBoxes 'ここでクラス変数を宣言、生成はしない 4 5 Set myChbes = New Collection 6 chBc = chBc 7 With Me 'チェックボックスのあるシート 8 For i = 1 To chBc 9 Set Chk = New clsLinkedCheckBoxes 'ここでクラスの実体を生成してクラス変数に格納 10 Chk.SetCtrl .OLEObjects("CheckBox" & i + chBc).Object, _ 11 .OLEObjects("CheckBox" & i).Object 12 myChbes.Add Chk '生成したクラスの実体をCollectionに追加 13 Next 14 End With 15End Sub

追記

上記の修正だけではうまく動作しませんでした。
いろいろ試してみた結果、
チェックボックスの生成と、クラスの登録を別に実行するとうまくいくようです。

Private Sub CommandButton2_Click()
の最後の、
Call clsLinked
を削除して、
コマンドボタンをシート上に追加して(CommandButton3)、そのクリック時で、clsLinked を実行するとうまくいきました。

また、
Public chBc As Longのパブリック変数を参照する方法は不安定なので、clsLinked内で件数を取得する方法に変更しました。

vba

1Private Sub CommandButton3_Click() 2 Call clsLinked 3End Sub 4 5Public Sub clsLinked() 6 Dim i As Long 7 Dim chk As clsLinkedCheckBoxes 'ここでクラス変数を宣言、生成はしない 8 Dim ChkCount As Long 9 10 Set myChbes = Nothing 11 Set myChbes = New Collection 12 13 ChkCount = (Range("A4").End(xlDown).Row - 4) * (Cells(1, Columns.Count).End(xlToLeft).Column \ 2) 14 15 With ActiveSheet 16 For i = 1 To ChkCount 17 Set chk = New clsLinkedCheckBoxes 'ここでクラスの実体を生成してクラス変数に格納 18 chk.SetCtrl .OLEObjects("CheckBox" & i + ChkCount).Object, _ 19 .OLEObjects("CheckBox" & i).Object 20 myChbes.Add chk 21 Next 22 End With 23 24 MsgBox "終了" 25End Sub

もし、1回ボタンをクリックするだけで、生成から、クラスの登録までしたい場合は、
Application.OnTime で clsLinked をタイマー実行するとうまくいきました。

OnTime は標準モジュールのプロシージャしか呼び出せないので、
clsLinkedプロシージャと Dim myChbes As Collection宣言を標準モジュールに移動させて、
CommandButton2_Click() の最後で、下記のように記述すればうまくいきます。

vba

1'前略 2 3' Call clsLinked これは削除 4 Application.OnTime Now + TimeValue("00:00:01"), Procedure:="clsLinked" 5 6End Sub

ユーザーフォームではこんな苦労することはないのですが、シート上のコントロールは扱いが難しいようです。

投稿2020/09/16 00:12

編集2020/09/17 17:40
hatena19

総合スコア33715

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

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

halmichi

2020/09/16 07:01

ありがとうございます。 chBc = chBc は何をしているでしょうか? というか、デバッグで見てみるとCommandButton1_Click()でchBcで格納した値がプロシージャを抜けると0になってしまっていました。仕方ないので、A3セルに格納してclsLinked()でまたそこを参照する形にしたのですが、やっぱり関連付けがされません。 デバッグでclsLinked()をたどらせると関連付けはされます。 clsLinked()が呼び出せてないんでしょうか? よろしくお願いいたします。
sinzou

2020/09/16 08:21 編集

キャンセルします
hatena19

2020/09/16 09:27

> chBc = chBc は何をしているでしょうか? これはテスト用のコードを書き換えたときに間違たものです。削除してください。 chBc はシートのモジュールで宣言してますよね。 いちおう動作確認はしたのですが、テスト用コードを書き換える時にどこか間違たかもしれません。 今は、時間かないので、後でもう一度確認してみます。
halmichi

2020/09/16 11:12

ありがとうございます。 よろしくお願いいたします。
halmichi

2020/09/17 07:27

動的配列でのコードも思いついたので書いてみました。 が、やっぱり動きません。 というか、これだとデバックでたどらせてもうまくいかないようです。 何がおかしいでしょうか? よろしくお願いいたします。
hatena19

2020/09/17 08:05

ちょっとなかなか時間が取れてくてすみません。今日の夜あたりに時間取れそうなので確認してみます。
hatena19

2020/09/17 17:41

うまくいく方法を見つけましたので、回答に追記しました。参照してください。
halmichi

2020/09/18 06:41

ありがとうございます。うまくいきました。 標準モジュールも、意識したことがなかったので勉強になりました。 ブログ読ませていただきましたが、先頭に変数を宣言しているのは初心者向けにわかりやすい形にしてくださっているものと理解しました。直前に宣言するのは、確かに便利な考えだと思うのでこれから真似してみようと思います。
hatena19

2020/09/18 07:35

うまくいきましたか。よかったです。 自分自身は、シート上にActiveXコントロールを置いてクラスに登録するということはほとんどしたことがなく、たいていユーザーフォーム上でやってますので、このような制限があるとは思わず引っかかりました。 今回の感想としては、シート上のActiveXコントロールはいろいろ扱いづらいので、ユーザーフォームで作成した方かいいとますます確信したということですね。たぶん、ユーザーフォームを使い慣れていないのだと思いますが、今回の要件もユーザーフォームの方が使いやすくものにできると思います。 今後のためにも頭の片隅にでも置いておいてください。
halmichi

2020/09/18 08:57

ありがとうございます。 一応この方向で完成までもっていきますが、時間を作ってユーザーフォーム型も作ってみて試してみてもらおうと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問