前提・実現したいこと
VBAで、指定した範囲のセルにチェックボックスを埋め込みたい
VBAで、選択範囲にチェックボックスを入れようとしたが、すべて選択範囲の左上に重ねて入ってしまう
の続きで詰まってしまっています。
上の画像のOLEオブジェクトのチェックボックスは1行2列が1単位で、左のチェックボックスがオンになると右が入力可能になり、左のチェックボックスがオフになると右のチェックボックスもオフにしたうえで入力不可にします。
VBA
1Private Sub CheckBox45_Click()'45が画像左上のチェックボックス、1がその右 連番で下に行って1列飛ばしで生成される 2 If CheckBox45.Value = False Then 3 CheckBox1.Value = False 4 CheckBox1.Enabled = False 5 Else 6 CheckBox1.Enabled = True 7 End If 8End Sub
これを画像の場合だと45から88までのプロシージャをクラスモジュールで作れればいいようなのですが、どうすればいいかわかりません。
複数のコントロールのイベントを一つのプロシージャにまとめる
擬似からの脱却
を参考にしたのですが、プロシージャの名前を入れるところで詰まっています。
わかる範囲でクラスモジュールの部分も書いてみました。
グローバル変数でチェックボックスの数をカウントしておいて、
Public chBc As Long
それをつかえば、うまくいくかなと思ったのですが。。。
VBA
1'チェックボックス番号の数字を格納する変数を宣言 2Private num As Integer 3 4Public Sub NewClass(ByVal c As MSForms.CheckBox, ByVal num As Integer) '初期化 5 Set chB = c 'チェックボックス格納 6 7 num = i 'チェックボックスの番号を変数に格納 8 9End Sub 10Sub chBctrl() 'チェックボックスコントロール 11 12 Dim num As New chB 13 14End Sub
よろしくお願いいたします。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答3件
0
ベストアンサー
2つのチェックボックスがワンセットになりますので、クラスモジュールにも2つのチェックボックスを持たせるようにするといいでしょう。
下記の私の回答が参考になると思います。
VBA - コード中で生成されたコマンドボタンの処理の指定|teratail
上記は、ラベルとコマンドボタンがワンセットになってます。
クラスモジュールの書き方
メニューの[挿入]-[クラスモジュール]をクリックしてクラスモジュールを追加します。
オブジェクト名を「clsLinkedCheckBoxes」とします。(機能の分かり安い名前にするといいでしょう。)
下記のように記述します。
vba
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
現状のチェックボックスのイベントプロシージャと比べると Chk1_Click の部分はほぼ同じということが分かると思います。
vba
1Private Sub CheckBox45_Click()'45が画像左上のチェックボックス、1がその右 連番で下に行って1列飛ばしで生成される 2 If CheckBox45.Value = False Then 3 CheckBox1.Value = False 4 CheckBox1.Enabled = False 5 Else 6 CheckBox1.Enabled = True 7 End If 8End Sub
あとはチェックボックスオブジェクトの宣言と、クラスのチェックボックスとシート上のチェックボックスを関連付けるメソッド SetCtrl を記述するだけです。
クラスとチェックボックスを関連付ける
これを画像の場合だと45から88までのプロシージャをクラスモジュールで作れればいいようなのですが、どうすればいいかわかりません。
CheckBox45 → CheckBox1
CheckBox46 → CheckBox2
CheckBox47 → CheckBox3
CheckBox48 → CheckBox4
・・・・
・・・・
CheckBox88 → CheckBox44
というようなペアになっているということですね。
ThisWorkbookのモジュールに下記のように記述してください。
vba
1Private Chkes(1 To 44) As New clsLinkedCheckBoxes 2 3Private Sub Workbook_Open() 4 Dim i As Long 5 With Worksheets("Sheet1") 'チェックボックスのあるシート 6 For i = 1 To 44 7 Chkes(i).SetCtrl .OLEObjects("CheckBox" & i + 44).Object, _ 8 .OLEObjects("CheckBox" & i).Object 9 Next 10 End With 11End Sub
いったん保存して閉じると、次回から開くとチェックボックスにクラスが関連付けられて、機能するようになります。
クラスでイベントが共通化できる仕組み
変数はデータを格納することができる器と考えることができます。
クラスは、データだけでなく処理(メソッド)も持つことができるもの(オブジェクト)です。
さらにクラスは、オブジェクトなので同じものを複数生成できるという特徴があります。
例えばエクセルのワークシートもオブジェクトの一種なので、好きなだけ追加することができますね。
クラスモジュールとはそのクラスオブジェクトの設計図と考えことができます。
たぶん、なんのことか理解できないと思いますので、実際のコードで解説します。
クラスモジュール clsLinkedCheckBoxes はすでに作成済みですね。
設計図はできています。
標準モジュールを追加して、下記のコードを記述してください。
VBA
1Option Explicit 2Private Chk As clsLinkedCheckBoxes 3 4Private Sub ClassTest() 5 With Worksheets("Sheet1") 'チェックボックスのあるシート 6 Set Chk = New clsLinkedCheckBoxes 'クラスの実体を生成 7 Chk.SetCtrl .CheckBox45, .CheckBox1 8 End With 9End Sub
これを実行すると、CheckBox45 と CheckBox1 が連動するようになります。
標準モジュールの先頭でクラス変数を宣言します。
プロシージャ内に記述してはダメです。プロシージャ内の変数はプロシージャの処理か終了すると消えてしまうからです。標準モジュールの先頭だとエクセルが開いてい間は存在します。
5行目の New clsLinkedCheckBoxes というのはclsLinkedCheckBoxesの設計図をもとに実体(インスタンスといいます)を生成するという処理になります。この実体が変数Chkに格納されます。
Chk.SetCtrl はクラスに実際のコントロール(チェックボックス)を登録する処理(メソッド)です。
クラスモジュールでSetCtrlをみると、
Set Chk1 = newChk1
Set Chk2 = newChk2
というようにクラス内変数に格納してます。
これで、クラスとコントロールが紐づけられ、コントロールにイベントが発生するとクラス内に記述してあるイベントプロシージャが実行されます。
前のコードは下記のように書くこともできます。
VBA
1Option Explicit 2Private Chk As New clsLinkedCheckBoxes 3 4Private Sub ClassTest() 5 With Worksheets("Sheet1") 'チェックボックスのあるシート 6 Chk.SetCtrl .CheckBox45, .CheckBox1 7 End With 8End Sub
Private Chk As New clsLinkedCheckBoxes はインスタンスの生成と変数の代入を同時にしています。
クラスは複数生成することができますので、
vba
1Option Explicit 2Private Chk1 As New clsLinkedCheckBoxes 3Private Chk2 As New clsLinkedCheckBoxes 4Private Chk3 As New clsLinkedCheckBoxes 5 6Private Sub ClassTest() 7 With Worksheets("Sheet1") 'チェックボックスのあるシート 8 Chk1.SetCtrl .CheckBox45, .CheckBox1 9 Chk2.SetCtrl .CheckBox46, .CheckBox2 10 Chk3.SetCtrl .CheckBox47, .CheckBox3 11 End With 12End Sub
と書けば一つの設計図をもとに複数のコントロールに同じ機能を持たせることができます。
3つぐらいのコントロールなら上記のように必要なだけ変数を書いて、紐づけるコードも繰り返し記述すればいいのですが、複数あるときは面倒ですよね。イベントプロシージャのコントロールの数だけ書くのと大差ないのでクラスにした意味がないです。
そこで配列変数を使います。
Private Chkes(1 to 44) As New clsLinkedCheckBoxes
と書くとChkes(1)からChkes(44)までの44個のクラスの実体が生成されて変数に格納されます。
クラスにコントロールを登録するのはFor Nextのループでできます。
vba
1Option Explicit 2Private Chkes(1 To 44) As New clsLinkedCheckBoxes 3 4Private Sub 複数コントロールの登録() 5 Dim i As Long 6 With Worksheets("Sheet1") 'チェックボックスのあるシート 7 For i = 1 To 44 8 Chkes(i).SetCtrl .OLEObjects("CheckBox" & i + 44).Object, _ 9 .OLEObjects("CheckBox" & i).Object 10 Next 11 End With 12End Sub
これを実行すると、
CheckBox45 → CheckBox1
CheckBox46 → CheckBox2
CheckBox47 → CheckBox3
・・・・
・・・・
CheckBox88 → CheckBox44
というようにチェックボックスが連動するようになります。
ブックを閉じるとこの関連付けは解除されますので、つぎ開いたときに適当なタイミングでまた上記の処理を実行する必要があります。
ThisWorkbookモジュールの Workbook_Openイベントはブックを開いたときに自動で実行されますので、ここに上記のコードを記述しておけば、ブックを開くと同時にチェックボックスは連動するようになります。
投稿2020/09/07 10:51
編集2020/09/09 04:53総合スコア34075
0
clsBpcaを使った場合でも上記リンクのようにするには、
標準モジュールに書くのでいいでしょうか?
clsBpca は、コントロールオブジェクト変数を宣言したモジュール内に
イベントプロシジャーを記述できるようにする事が主眼のクラスです。
イベントプロシジャーを記述するので、必然的にオブジェクトモジュール
(UserForm , ThisWorkbook , Sheet1モジュール等)での利用となります。
設問を見直しましたら、「右チェックボックス」の入力可否のみで、
それもクラスモジュール内で直接処理すれば十分であり、Click/Change等の
様々なイベントプロシジャー引っ張って来る必要は無いようなので、
本件では clsBpca は不要ですね。
混乱させてしまったようで申し訳ないです。
クラスモジュールで、もう少しスッキリできないものかと、
最初に考えた案(実際は失敗でしたが・・・)
--- 標準モジュール --- Private CheckBoxes() As clsCheckBoxes Public Sub CreateCheckBoxGroup() Dim j As Integer Dim k As Integer Dim Rng As Range Dim RowSize As Integer Dim ColumnSize As Integer 'サンプルで基点[A3セル] 縦2横3の例 RowSize = 2 ColumnSize = 3 ReDim CheckBoxes(1 To RowSize, 1 To ColumnSize) 'チェックボックスの生成 Application.ScreenUpdating = False With Worksheets("Sheet1") For j = 1 To RowSize For k = 1 To ColumnSize Set Rng = .Range("A3").Offset(j - 1, (k - 1) * 2) '左Boxセル Set CheckBoxes(j, k) = New clsCheckBoxes CheckBoxes(j, k).CreateCheckBox Rng '右Boxセルはクラス内で設定 DoEvents Next k Next j End With Application.ScreenUpdating = True End Sub Sub TEST() MsgBox "[1,1] 左=" & CheckBoxes(1, 1).LeftValue & _ " , 右=" & CheckBoxes(1, 1).RightValue & vbCrLf & _ "[2,3] 左=" & CheckBoxes(2, 3).LeftValue & _ " , 右=" & CheckBoxes(2, 3).RightValue End Sub
--- clsCheckBoxes クラスモジュール --- Private WithEvents ChkLeft As MSForms.CheckBox Private ChkRight As MSForms.CheckBox Public Sub CreateCheckBox(ByRef RngLeft As Range) Dim ParentWksh As Worksheet Dim RngRight As Range Set ParentWksh = RngLeft.Parent Set RngRight = RngLeft.Offset(0, 1) '--- 左チェックボックス --- With ParentWksh.OLEObjects.Add _ (ClassType:="Forms.CheckBox.1", _ Link:=False, DisplayAsIcon:=False) .Width = RngLeft.Width - 5 .Height = RngLeft.Height - 5 '対象セルの中央に配置 .Top = RngLeft.Top + (RngLeft.Height - .Height) / 2 .Left = RngLeft.Left + (RngLeft.Width - .Width) / 2 With .Object .Caption = "" .BackColor = vbWhite .Value = False End With Set ChkLeft = .Object End With '--- 右チェックボックス --- With ParentWksh.OLEObjects.Add _ (ClassType:="Forms.CheckBox.1", _ Link:=False, DisplayAsIcon:=False) .Width = RngRight.Width - 5 .Height = RngRight.Height - 5 '対象セルの中央に配置 .Top = RngRight.Top + (RngRight.Height - .Height) / 2 .Left = RngRight.Left + (RngRight.Width - .Width) / 2 With .Object .Font.Size = 9 .Caption = "リハ" .ForeColor = vbBlack .BackColor = vbWhite .Value = False .Enabled = False End With Set ChkRight = .Object End With End Sub Public Property Get LeftValue() As Boolean LeftValue = ChkLeft.Value End Property Public Property Get RightValue() As Boolean RightValue = ChkRight.Value End Property '左チェックボクスで[右チェックボクス]の入力可否を設定 Private Sub ChkLeft_Click() If (ChkLeft.Value = False) Then ChkRight.Value = False ChkRight.Enabled = False Else ChkRight.Enabled = True End If End Sub
チェックボックス作成の都度「VBAプロジェクト」がリセット
される事により、モジュールレベル変数 CheckBoxes() が
空になる為、折角セットしたクラスオブジェクトが1個も残らない
状況となり、イベントも捕れないし、値も取れない。
結局、シートではチェックボックスの作成とクラスの設定は分ける
しかないと判った(という事が既に述べられている事に早く気付けば良かった )
修正案
--- 標準モジュール --- 'サンプルで基点[A3セル] 縦2横3の例 Private Const RowSize As Integer = 2 Private Const ColumnSize As Integer = 3 Private CheckBoxes() As clsCheckBoxes Public Sub CreateCheckBoxGroup() Dim j As Integer Dim k As Integer Dim Rng As Range 'チェックボックスの生成 Application.ScreenUpdating = False With Worksheets("Sheet1") For j = 1 To RowSize For k = 1 To ColumnSize Set Rng = .Range("A3").Offset(j - 1, (k - 1) * 2) '左Boxセル Call CreateCheckBox(j, k, Rng) '右Boxセルはルーチン内で設定 DoEvents Next k Next j End With Application.ScreenUpdating = True End Sub Public Sub CreateCheckBox(ByVal RowIdx As Integer, ByVal ColIdx As Integer, ByRef RngLeft As Range) Dim ParentWksh As Worksheet Dim RngRight As Range Set ParentWksh = RngLeft.Parent Set RngRight = RngLeft.Offset(0, 1) '--- 左チェックボックス --- With ParentWksh.OLEObjects.Add _ (ClassType:="Forms.CheckBox.1", _ Link:=False, DisplayAsIcon:=False) .Width = RngLeft.Width - 5 .Height = RngLeft.Height - 5 '対象セルの中央に配置 .Top = RngLeft.Top + (RngLeft.Height - .Height) / 2 .Left = RngLeft.Left + (RngLeft.Width - .Width) / 2 With .Object .Caption = "" .BackColor = vbWhite .Value = False End With 'チェックボクスの名前を変更( CheckBoxLeft02_05 等) .Name = "CheckBoxLeft" & Format(RowIdx, "00") & "_" & Format(ColIdx, "00") End With '--- 右チェックボックス --- With ParentWksh.OLEObjects.Add _ (ClassType:="Forms.CheckBox.1", _ Link:=False, DisplayAsIcon:=False) .Width = RngRight.Width - 5 .Height = RngRight.Height - 5 '対象セルの中央に配置 .Top = RngRight.Top + (RngRight.Height - .Height) / 2 .Left = RngRight.Left + (RngRight.Width - .Width) / 2 With .Object .Font.Size = 9 .Caption = "リハ" .ForeColor = vbBlack .BackColor = vbWhite .Value = False .Enabled = False End With 'チェックボクスの名前を変更( CheckBoxRight02_05 等) .Name = "CheckBoxRight" & Format(RowIdx, "00") & "_" & Format(ColIdx, "00") End With End Sub 'チェックボクスの名前は[ CheckBoxLeft02_05 , CheckBoxRight02_05 等]になっている '数字は[縦Index_横Index ] Public Sub ClassRegister() Dim j As Integer Dim k As Integer Dim strIdx As String Dim obj As MSForms.CheckBox ReDim CheckBoxes(1 To RowSize, 1 To ColumnSize) With Worksheets("Sheet1") For j = 1 To RowSize For k = 1 To ColumnSize Set CheckBoxes(j, k) = New clsCheckBoxes strIdx = Format(j, "00") & "_" & Format(k, "00") Set CheckBoxes(j, k).ItemLeft = .OLEObjects("CheckBoxLeft" & strIdx).Object Set CheckBoxes(j, k).ItemRight = .OLEObjects("CheckBoxRight" & strIdx).Object Next k Next j End With End Sub Sub TEST() MsgBox "[1,1] 左=" & CheckBoxes(1, 1).LeftValue & _ " , 右=" & CheckBoxes(1, 1).RightValue & vbCrLf & _ "[2,3] 左=" & CheckBoxes(2, 3).LeftValue & _ " , 右=" & CheckBoxes(2, 3).RightValue End Sub
--- clsCheckBoxes クラスモジュール --- Private WithEvents ChkLeft As MSForms.CheckBox Private ChkRight As MSForms.CheckBox Public Property Set ItemLeft(ByRef Item As MSForms.CheckBox) Set ChkLeft = Item End Property Public Property Set ItemRight(ByRef Item As MSForms.CheckBox) Set ChkRight = Item End Property Public Property Get LeftValue() As Boolean LeftValue = ChkLeft.Value End Property Public Property Get RightValue() As Boolean RightValue = ChkRight.Value End Property '左チェックボクスで[右チェックボクス]の入力可否を設定 Private Sub ChkLeft_Click() If (ChkLeft.Value = False) Then ChkRight.Value = False ChkRight.Enabled = False Else ChkRight.Enabled = True End If End Sub
投稿2020/10/02 16:33
総合スコア51
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
こんにちは。
「擬似からの脱却」サイトの角田です。
2020/9/7投稿という事はサンプルコード($11)に
シート上のコントロールに対する利用例を追加記載した後
ですが、気付いて頂けなかったですかね・・・
汎用クラス(clsBpca)を利用すると以下のようなマクロになります。
(補)
clsBpca はブラックボックスとして、clsBpca 自体のマクロコードを
理解(解読)する必要はありませんよ。只、利用するだけです。
VBA
1【 以下は全てThisWorkbook モジュールに記述します 】 2 3 4Private WithEvents Bpca_ShChk_Left As clsBpca 5Private WithEvents Bpca_ShChk_Right As clsBpca 6 7'--------------------------------------------------------- 8Private Sub Workbook_Open() 9Dim j As Integer 10 11 Set Bpca_ShChk_Left = New clsBpca ' インスタンスの生成 12 Set Bpca_ShChk_Right = New clsBpca 13 14 ' [ ワークシート.OLEObjects(コントロール名).Object ]のスタイルで指定します 15 With Worksheets("Sheet1") 16 For j = 1 To 44 17 Bpca_ShChk_Left.Add .OLEObjects("CheckBox" & (j + 44)).Object 18 Bpca_ShChk_Right.Add .OLEObjects("CheckBox" & j).Object 19 20 'この設定により左(45-88)/右(1-44)のチェックボックスの 21 'Index値は共に[1~44]となり、同じIndex値の左/右が対応する事になる。 22 Next j 23 End With 24 25 Bpca_ShChk_Left.Rgst BPCA_Change 26 Bpca_ShChk_Right.Rgst BPCA_Change 27End Sub 28 29'--------------------------------------------------------- 30Private Sub Workbook_BeforeClose(Cancel As Boolean) 31'VBAプロジェクトのリセットにより、 32'オブジェクト変数(Bpca_ShChk_Left,Bpca_ShChk_Right)が初期化されている 33'場合(実行時エラーになる)に備えて[ On Error Resume Next ]を必ず記述する 34 On Error Resume Next 35 Bpca_ShChk_Left.Clear 36 Bpca_ShChk_Right.Clear 37 Set Bpca_ShChk_Left = Nothing 38 Set Bpca_ShChk_Right = Nothing 39End Sub 40 41'--------------------------------------------------------- 42Private Sub Bpca_ShChk_Left_Change(ByVal Index As Integer) 43 If (Bpca_ShChk_Left(Index).Value = False) Then 44 Bpca_ShChk_Right(Index).Value = False 45 Bpca_ShChk_Right(Index).Enabled = False 46 Else 47 Bpca_ShChk_Right(Index).Enabled = True 48 End If 49End Sub 50
投稿2020/09/27 17:08
編集2020/09/30 10:35総合スコア51
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/09/29 01:43
2020/09/29 05:19
2020/09/30 00:19 編集
2020/09/30 10:56
2020/10/02 08:15
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/09/08 12:37
2020/09/08 12:50
2020/09/08 23:35
2020/09/09 04:56 編集
2020/09/09 04:57
2020/09/10 06:06
2020/09/10 06:55
2020/09/10 23:03 編集
2020/09/12 03:36
2020/09/12 04:06
2020/09/12 04:56