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

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

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

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

Q&A

解決済

3回答

4553閲覧

任意の数だけ自動生成されたOLEオブジェクトのチェックボックスを連動させるためにクラスモジュールを使いたい

halmichi

総合スコア12

VBA

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

0グッド

1クリップ

投稿2020/09/07 09:55

編集2020/09/12 02:15

前提・実現したいこと

生成されたOLEオブジェクト

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ページで確認できます。

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

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

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

guest

回答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
hatena19

総合スコア33715

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

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

halmichi

2020/09/08 12:37

ありがとうございます。Chk1、2に、OLEオブジェクトのチェックボックスを入れるようにして、さらにそれは、クリックしたときのプロシージャの名前の文字列としても扱えるということでしょうか? いただいたコードをコピペしてみたのですが、チェックボックスは連動しておらず、デバッグでは「Sub または Function が定義されていません。」というエラーが出ます。何かプロシージャを呼び出しているわけでもないし、クラスモジュールの名前もlsLinkedCheckBoxesに変えているし、何が考えられるでしょうか?よろしくお願いいたします。
hatena19

2020/09/08 12:50

「クリックしたときのプロシージャの名前の文字列としても扱えるということでしょうか?」というが何を意味しているのかわかりません。 たぶんクラスモジュールの機能を誤解しているように思います。プロシージャの名前の文字列として扱う必要はなにもないです。 提示のコードだけで機能するはずです。 > クラスモジュールの名前もlsLinkedCheckBoxesに変えているし、 正しくは clsLinkedCheckBoxes ですけど、間違えてませんか。コピペミスならいいですが。 > 「Sub または Function が定義されていません。」というエラーが出ます。 そのとき、どの行が反転表示されますか。
halmichi

2020/09/08 23:35

ありがとうございます。 クラスモジュールを勉強して、イメージしていたものは Private Sub CheckBox45_Click() の45の部分を45から88まで連番で作りたい CheckBox45_Clickは、プロシージャの名前で文字列のはずなので文字列を操作すればいいはず という感じで、 クラスモジュールを使って文字列を操作してプロシージャ名に格納する方法が、検索しても見つけられない ではまっていた感じです。 すみません、コピペミスです。clsLinkedCheckBoxes にできています。 Private Sub Workbook_Open()の行をブレイクポイントにすると 4行下の、Chkes(i).SetCtrl のChkesが反転されます。 チェックボックスのあるシートも、Sheet1です。 よろしくお願いいたします。
hatena19

2020/09/09 04:56 編集

ThisWorkbookのモジュールの先頭に Private Chkes(1 To 44) As New clsLinkedCheckBoxes は記述してありますか。 また、メニューの[デバッグ]-[VBAProjectのコンパイル]をクリックして、他にコンパイルエラーがでないか確認してください。 エラーがでないとこを確認したら、[ファイル]-[ブック名の上書き保存]をクリックしてください。 その後、ブックをいったん閉じて再度開いてください。 するとチェックボックスは連動しているはずです。 クラスモジュールに関しては間違って理解しているようなので、回答にクラスモジュールの仕組みを簡単に解説しておきます(執筆中ですのでしぱらくお待ちください)。
hatena19

2020/09/09 04:57

クラスモジュールについて解説を追加しましたので、参照してください。
halmichi

2020/09/10 06:06

ありがとうございます。 わかりやすい説明に感激しています。 クリック時に動くようにするには プロシージャの名前を文字列で作るのではなく、クラスモジュールのWith Eventsで指定しているんですね。 おっしゃる通りモジュールの先頭に置いていませんでした。 直したら正しく動いてくれました。 このチェックボックスは、作業ごとに作り入力して時間割を作成した後は捨ててしまってもいいものなので、チェックボックス生成時に関連付けもしてしまいたいのですが、今のところうまくいっていません。 もう少し自力で考えてみてわからなかったらまた質問しようと思います。
hatena19

2020/09/10 06:55

下記の私の回答が参考になると思います。 VBA - コード中で生成されたコマンドボタンの処理の指定|teratail https://teratail.com/questions/288841#reply-409488 上記はユーザーフォーム上にコマンドボタンとラベルのセットを複数生成するというものですので、すこし違いますが、生成時に関連付けするところとか参考になると思います。。 今回の回答はコントロールの数が固定ということで配列にクラスを格納しましたが、もし、コントロール数がその時々で変わるということならCollectionオプジェクトに格納すると個数の可変に対応できますので、Collectionの使い方も調べるといいでしょう。
hatena19

2020/09/10 23:03 編集

処理全体がわからないので推測ですか、自動生成して配置するならシート上でなくユーザーフォームで作成した方がいいように思えます。ユーザーフォームについても検討されてみては。
halmichi

2020/09/12 03:36

ありがとうございます。 Collectionオブジェクトを勉強中です。 月一回の処理、 列ヘッダーに日付、行ヘッダーに名前があり、月によって数も内容も変わります。(画像を追加しました。) 任意の日付と名前を入力した後、 日付の後ろに列を挿入する 2つのチェックボックスを生成する そのチェックボックスに任意の入力をした後 チェックボックスの下の行に、行を挿入して時間割を作成する といった具合です。 名前が多くなりすぎると表に埋め込んであるほうがわかりやすいかと思ってこの形にしています。 ユーザーフォームでも大丈夫でしょうか?
hatena19

2020/09/12 04:06

その辺は好みになりますので、シート上でもいいとは思います。 ユーザーフォームで表形式らしくするなら、フレームの上にチェックボックスを配置して縦横に並べればそれらしくなります。手作業でするなら面倒ですが、コードで生成配置するなら数行コードを追加するだけです。 データ数が多い場合は、ユーザーフォームにスクロールバーを表示させることもできます。 私はAccessでの開発がメインですので、データはテーブルに格納、閲覧や入力はフォームでという分業制になれているのでそういう発想になりがちです。フォームの方かアプリケーションっぽくみえますし。 でも、シート上できっちり作成しているシステムもありますのでこれはもうプログラマーの好みですね。
halmichi

2020/09/12 04:56

ありがとうございます。 ユーザーフォームも勉強してみます。 わからなかったらまたお願いします。
guest

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

AddinBoxTsunoda

総合スコア51

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

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

halmichi

2020/10/03 11:57

>clsBpca は、コントロールオブジェクト変数を宣言したモジュール内に >イベントプロシジャーを記述できるようにする事が主眼のクラスです。 ようやく理解しました。記事自体を間違った先入観で読んでしまっているので混乱していたようです。 今後必要になるときもあると思うのでもう少し読み込んでみます。 コード自体もほぼ理解できたと思います。 ありがとうございました。
guest

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
AddinBoxTsunoda

総合スコア51

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

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

halmichi

2020/09/29 01:43

ありがとうございます。 調べる際、念頭に置いていたのが下記のイメージでした。 Private Sub CheckBox45_Click() の45の部分を45から88まで連番で作りたい CheckBox45_Clickは、プロシージャの名前で文字列のはずなので文字列を操作すればいいはず という感じで、 クラスモジュールを使って文字列を操作してプロシージャ名に格納する方法が、検索しても見つけられない という感じでした。的が外れていますが。 記事を読ませていただいて、5、6ページ目までは何とかついていけたのですが、その後想定していたイメージと離れすぎて、目は通したのですが理解できていません。 今夜は時間が取れそうなので試してみます。
AddinBoxTsunoda

2020/09/29 05:19

> CheckBox45_Clickは、プロシージャの名前で文字列のはずなので > 文字列を操作すればいいはずという感じで、 > クラスモジュールを使って文字列を操作してプロシージャ名に > 格納する方法が、検索しても見つけられない > という感じでした。 そもそも、プロシジャー名はVBAで扱う「文字列」ではないですから。 メモリ上の『アドレス』に変換されていますから。 もし無理やりに行なうとすると、恐らく プロシジャの実行アドレスを指すポインタを書き換える アセンブラコードをバイナリで記述した定数を用意して云々 というような事になるのでしょうね。 私は知らないけど・・・
halmichi

2020/09/30 00:19 編集

昨夜コピペしてみたのですが、 Bpca_ShLbl_Left.Rgst BPCA_Change の行で変数が定義されていないと出るのと、 ファイルを開いたときにワークシート上のOLEオブジェクトを取得できないとのエラーが出ます。 今まで何も考えずシートモジュールにコードを書いてきました。標準モジュールとブックモジュールとの違いを少し調べてみましたが、シートモジュールに書かれていることがエラーの原因かはわかりませんでした。そもそもなぜ今回ブックモジュールに書くのかもよくわかりません。少なくとも、sheet1が作業シートなのでWith Worksheets("Sheet1")で問題ないと思うのですが。。。 よろしくお願いいたします。
AddinBoxTsunoda

2020/09/30 10:56

すみません。 Workbook_Open 内の最後の > Bpca_ShLbl_Left.Rgst BPCA_Change > Bpca_ShLbl_Right.Rgst BPCA_Change は、ShLbl のところを SnChk に修正してください(スレッドは修正済)。 > ファイルを開いたときにワークシート上の > OLEオブジェクトを取得できないとのエラーが出ます。 上記修正をして、Sheet1上に CheckBox1~CheckBox88 が 配置されていれば、そのエラーは出ない筈です。 CheckBox1~CheckBox88 は、ちゃんと在りますか? > そもそもなぜ今回ブックモジュールに書くのかもよくわかりません。 ワークブックを開いたら、即、チェックボックスが動作するようにする為には、 WorkBook_Open で初期処理を実行して、Sheet1上のチェックボックスを clsBpcaのオブジェクト変数に取り込む必要があります。 WorkBook_Open で処理するのですから、Bpca_ShChk_Left/Bpca_ShChk_Right は WorkBook_Open がある ThisWorkbook モジュールにて定義する必要があります。 補:Thisworkbook や Sheet1 モジュールなどで、Public宣言で変数定義しても、 そのモジュール以外では参照できません(Public と書いても Private 扱いです)。 clsBpca オブジェクトが ThisWorkbook モジュールで定義されているので、 当然、そのイベントプロシジャーも Thisworkbook モジュールで記述する事になります。
halmichi

2020/10/02 08:15

ありがとうございます。 ご指摘の通り、いろいろ試している間にチェックボックスを少なくしてしまっていました。 直したら正しく動作してくれました。 最終的にはチェックボックスを生成した後、そのまま関連付けるようにしたいです。 https://teratail.com/questions/292056 clsBpcaを使った場合でも上記リンクのようにするには、標準モジュールに書くのでいいでしょうか? 今後、生成したチェックボックスを評価したいのですが、 行や列だけ指定して評価ができなさそうなので、 (指定列のTrueの数をカウント、指定行に一つでもtrueがあれば処理するなど) 生成の基準にした行と列の数から、 評価したい行、列に対応するチェックボックスの番号を クラスモジュールで生成したコレクションオブジェクトに格納して評価しようと思っています。 その際、clsBpcaを使うメリットはありますか? 今回のケースに必要がなくても、オブジェクト指向の理解は深めたいのでもう一度サイトを読ませていただこうと思いますが、一般的なオブジェクト指向に似た動作をVBAでもさせるコンセプトのように感じました。 ほかの言語で理解してからのほうが早いでしょうか? 並行してPythonも勉強中で、もう少しでオブジェクト指向のさわりに入りそうです。 よろしくお願いいたします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問