Excel (VBA)

Excel VBAに関するフォーラムです。
  • 解決済みのトピックにはコメントできません。
このトピックは解決済みです。
質問

 
(Windows 10 Pro : Excel 2016)
UserForm 複数のCheckBoxの一括制御
投稿日時: 18/01/16 17:07:45
投稿者: lovely.wolf

ユーザーフォームのチェックボックスの制御についてお知恵を拝借させてください。
 
UserForm1_inputの中にマルチページで8枚のタブを設けています。
各タブにはチェックボックスが13個づつ、計104個のチェックボックスを配置しました。
各チェックボックスの名称は「CB*02_テストAll」または「CB*02_テストm」です。
(*:マルチページのインデックス番号、m:1月〜12月の月数 例:CB302○○11)
 
【やりたいこと】
1.最後の3文字がAllのチェックボックス
 残りの12か月分のチェックボックスも連動してTrue/Falseを変更する。
2.All以外のチェックボックス
 True:残りの11個を確認し、すべてがTrueならAllもTrueにする。
 False:AllはFalseとする。
 
【現在の状況】
AllはFalseの場合のみ、狙った通りに動作しています。(Trueは見た目、無反応)
各月はTrueにする時は狙い通り、Falseにした時に一括で13個がFalseになってしまいます。
 
なぜ上記のような動作をしてしまうのかは理解できているつもりですが、解決策が
思い浮かびません。今後ページが増える可能性、チェックボックスが増える可能性も
考慮しなければならないため、できるだけシンプルに作りたいのですが...。
 
よろしくお願いいたします。
 
 

UserForm1_input

Private myCheckBox(1 To 104) As New Class6

Private Sub UserForm_Initialize()

    Dim i As Integer, m As Integer, cntC As Integer

    For i = 1 To 8  'Page 1 to 8
        For m = 0 To 12 'All+12ヶ月
            cntC = cntC + 1
            If m = 0 Then myCheckBox(cntC).SetCtrl Me("CB" & i & "02_テストAll")
            If m <> 0 Then myCheckBox(cntC).SetCtrl Me("CB" & i & "02_テスト" & m)
        Next m
    Next i    

End Sub


 
Class6

Private WithEvents Target As MSForms.CheckBox
 
Public Sub SetCtrl(new_ctrl As MSForms.CheckBox)
  Set Target = new_ctrl
End Sub
 
Private Sub Target_Click()
    
    Dim objUForm As Object, m As Integer, f As Boolean, p As String, n As String
    
    Set objUForm = UserForm1_input
    p = Mid(Target.Name, 3, 1)
    n = "CB" & p & "02_テスト"
    
    If Target.Name Like n & "*All" Then
        For m = 1 To 12
            objUForm.Controls(n & m) = Target.Value
        Next m
    Else
        If Target.Value = True Then
            f = True
            For i = 1 To 12
                If objUForm.Controls(n & i).Value = False Then f = False
            Next i
            objUForm.Controls(n & "All").Value = f
        Else
            objUForm.Controls(n & "All").Value = False
        End If
    End If
    
    Set objUForm = Nothing
    
End Sub

回答
投稿日時: 18/01/16 17:57:20
投稿者: WinArrow
投稿者のウェブサイトに移動

ステップ実行してみましたか?

投稿日時: 18/01/16 18:15:51
投稿者: lovely.wolf

はい。確認しました。
あっちに飛んだり、こっちに飛んだりで追っかけにくいのですが...
Allの制御が絡むと意図しない動作になります。
 
例えばAllにチェックが入っていて、9月のみをFalseにしたい場合、
9月とAllのチェックのみFalseにしたいのですが、連動してすべてがFalseに
なってしまう...
 
AllをTrueにしようとすると、単月のチェックボックスを確認
1月から順にチェックを入れようとするが、其の他の月がFalseだから、
結果すべてがFalseになってしまう...
 
というような感じです。
私のロジックが間違っているのは確かなのですが、ではどう修正して
良いのかわからず、です。

投稿日時: 18/01/16 18:33:58
投稿者: lovely.wolf

すみません。
急きょ退社しなければならなくなってしまったので、明日以降あらためて
お付き合いいただけるとうれしいです。

回答
投稿日時: 18/01/16 20:13:50
投稿者: WinArrow
投稿者のウェブサイトに移動

クラスモジュールの中で、
クリックしたチェックボックスを含めて、
Valueをセットしているところがあります。
 
クラスモジュールの中で、
チェックボックスのVaueを変更しないようにするか、
または、イベント発生を抑制するか
 
どちらかの対処が必要です。
 
8*13=104のような沢山のコントロールを作成せずに
2〜3個くらいで
ステップ実行すれば動きが分かりますよ!
 

投稿日時: 18/01/17 11:48:01
投稿者: lovely.wolf

WinArrowさん、ありがとうございます。
 
私の今の能力ではうまくできないこと、本来の目的は入力を楽にしたいだけ...
という理由で「チェックボックスAll」にこだわるのは止め
・チェックボックスAllは廃止
・代わりに「切替え(コマンドボタン)」を各ページに配置
12か月のチェックボックスをコマンドボタンより一括で制御する事でしのげました。
とりあえず業務上では解決です。
 
後学のために質問を継続させてください。
 

引用:

   If Target.Value = True Then
            f = True
            For i = 1 To 12
                If objUForm.Controls(n & i).Value = False Then f = False
            Next i
            objUForm.Controls(n & "All").Value = f   ←ココ
        Else
            objUForm.Controls(n & "All").Value = False ←ココ
        End If

 
引用:
クラスモジュールの中で、
クリックしたチェックボックスを含めて、
Valueをセットしているところがあります。

 
「ココ」の部分ですよね?
しかし、AllがTrueだった場合、ココの部分がないと1月分だけチェックを外した時に
AllはTrueのまま...となってしまうと思います。
 
引用:
クラスモジュールの中で、
チェックボックスのVaueを変更しないようにするか、
または、イベント発生を抑制するか

 
イベント抑制となるとEnableEventsくらいしか思い浮かばないのですが、他に何か
ありますでしょうか。それとも、EnableEventsを細かくFalse/Trueすべきだったんで
しょうか?
 
そもそも、All+12か月のチェックボックスって考え方が間違っていたのでしょうか。。。

回答
投稿日時: 18/01/17 12:12:21
投稿者: mokutachi

全部読むのはきついので
>EnableEvents
についてだけ。
 
>EnableEvents
はExcel.Application配下のプロパティです。
つまり、Excel.Applicatin配下のイベントのみ制御可能です。
 
UserformはMsForms配下なので無関係です。
 

投稿日時: 18/01/17 13:12:00
投稿者: lovely.wolf

mokutachiさん、ありがとうございます。
 

引用:
UserformはMsForms配下なので無関係です。

 
いろいろなサンプルをいつも見よう見真似でアレンジしていて、
詳しいことがわからず。
普通に使えると思いこんでました。
勉強になります。ありがとうございました。
 
そっか...
Application.○○はユーザーフォームじゃ使えないのか...

回答
投稿日時: 18/01/17 14:03:42
投稿者: WinArrow
投稿者のウェブサイトに移動

>そもそも、All+12か月のチェックボックスって考え方が間違っていたのでしょうか。。。
 
 
チェックボックスの戸数の問題ではありません。
テストは少ない方がやりやすいということと、
少ない個数でテストし、意図したようにできた後で、個数を増やせばよいということです。
若し、駄目だったら、せっかく作ったフォームのデザインから変更することになります。
この方が効率が良いということです。
 
 
ところで、Application.Enabled が使えないので、
標準モジュールにイベント制御用の変数をおきます。

Public Flag As Boolean
 
クラスモジュールの中で、チェックボンクスのValueを変更する前後に
Flagをチェックします。
 

    If Falg = False Then
        Flag = True
       チェックボックスのValue変更
    Flag = False
  End If
こんな感じです。

回答
投稿日時: 18/01/17 14:31:49
投稿者: WinArrow
投稿者のウェブサイトに移動

蛇足
↓のコードを簡潔にしましょう

引用:
Set objUForm = UserForm1_input
    p = Mid(Target.Name, 3, 1)
    n = "CB" & p & "02_テスト"
     
    If Target.Name Like n & "*All" Then
        For m = 1 To 12
            objUForm.Controls(n & m) = Target.Value
        Next m
    Else
        If Target.Value = True Then
            f = True
            For i = 1 To 12
                If objUForm.Controls(n & i).Value = False Then f = False
            Next i
            objUForm.Controls(n & "All").Value = f
        Else
            objUForm.Controls(n & "All").Value = False
        End If
    End If
     
    Set objUForm = Nothing


 
   p = Mid(Target.Name, 3, 1)
    n = "CB" & p & "02_テスト"
    With UserForm1_input
        If Target.Name Like n & "*All" Then
            For m = 1 To 12
                .Controls(n & m) = Target.Value
            Next m
        Else
            If Target.Value = True Then
                f = True
                For i = 1 To 12
                    If .Controls(n & i).Value = False Then f = False
                Next i
                .Controls(n & "All").Value = f
            Else
                .Controls(n & "All").Value = False
            End If
        End If
    End With

回答
投稿日時: 18/01/17 14:54:23
投稿者: WinArrow
投稿者のウェブサイトに移動

投稿日時: 18/01/17 11:48:01
の記事の中で
>ココ
が2カ所に書かれているが、
3ヶ所ではなかったっけ?
 
 

投稿日時: 18/01/17 18:54:29
投稿者: lovely.wolf

WinArrowさん、ありがとうございます。
 

WinArrow さんの引用:
投稿日時: 18/01/17 11:48:01
の記事の中で
>ココ
が2カ所に書かれているが、3ヶ所ではなかったっけ?

 
先ほどの箇所より上のほう、Allを制御する部分を除いてたので計3か所です。
 
 
業務とは別件になったので、改めてテスト用のファイルを作成しました。
ユーザーフォームに4つ(Allと1〜3月)のチェックボックスのみの
シンプルな構成です。現在は下記のコードでテストしています。
 
Private Sub Target_Click()
    
    Dim m As Integer, f As Boolean, n As String
    
    n = "CB" & "_test"
    With UserForm1
        If Target.Name = n & "All" Then
            For m = 1 To 3 '12month
                If falg = False Then
                    flag = True: .Controls(n & m).Value = Target.Value: flag = False
                End If
            Next m
        Else
            If Target.Value = True Then
                f = True
                For m = 1 To 3 '12month
                    If .Controls(n & m).Value = False Then f = False
                Next i
            Else
                f = False
            End If
            If falg = False Then
                flag = True: .Controls(n & "All").Value = f: flag = False
            End If
        End If
    End With
    
End Sub

 
基本的には、今までと同じ動作のような感じがます。
フラグの判定方法を間違えているのでしょうか。。。
 

回答
投稿日時: 18/01/17 19:22:47
投稿者: WinArrow
投稿者のウェブサイトに移動

> flag = True: .Controls(n & m).Value = Target.Value: flag = False
 
↑このような記述は避けた方がよい
手間かもしれませんが、↓
                    flag = True
          .Controls(n & m).Value = Target.Value
          flag = False
 
可読性が向上→メンテナンス性向上
 

回答
投稿日時: 18/01/17 19:25:46
投稿者: WinArrow
投稿者のウェブサイトに移動

>基本的には、今までと同じ動作のような感じがます。
ステップ実行(1ステップづつ「F8」で進めて)いくと、違いがわかります。

投稿日時: 18/01/18 10:20:12
投稿者: lovely.wolf

WinArrowさん、ありがとうございます。
 
ステップ実行はしてたのですが、違いがよくわからず...で、引き続き
いろいろ試してみたかったのですが、急ぎの案件がいくつも
入ってきてしまい、しばらく時間がとれなくなってしまいました。
 
いったん解決とし、時間が取れるようになったら改めて勉強したいと思います。
長々とお付き合いくださいまして、ありがとうございました。
また何かありましたら、よろしくお願いいたします。