CheckBox1.ValueでCheckBoxオブジェクトのValueプロパティを取得します。ONならTrue、OFFならFalseを返します。
今回はチェックボックスのON/OFFを判定して、それぞれに処理を連動させるマクロを作成します。チェックボックスとはダイアログ等で見るアレの事です。
コレの事です。
普通のボタンと違ってONとOFFの状態が判るのが利点です。制御はそんなに難しくありません。最後まで読んでチェックボックスを実装しましょう。
覚える用語
- For Each~Nextステートメント
いわゆる「ループ処理」です。記述した内容を繰り返し処理してくれる命令文です。
- If Thenステートメント
エクセルのIF関数と同じような処理です。条件式のTrue、Falseを判定し、分岐処理します。
準備
今回はエクセルの名前の定義を利用するので適当な範囲を定義して下さい。本記事では年間カレンダーをサンプルにしていますが、サンプルは何でも構いません。皆さんがチェックボックスを実装したいファイルを利用して、適宜読み替えてください。
数式タブの名前の定義をクリックするとダイアログが現れます。
名前を付け、参照範囲を設定して下さい。サンプルは月曜としています。
サンプルはカレンダーの月曜部分を月曜と定義していますが、皆さんがテストする分には適当な範囲で構いません。ただし、今回はループ処理を使用するので範囲がある程度あった方がマクロの動きが分かりやすいと思います。
チェックボックスの値を取得、ON/OFFを判定して書式変更を連動させる
チェックボックスに連動する処理の内容
- チェックボックスオンで定義した範囲全てを赤字,太字,下罫線(赤)に変更する
- チェックボックスオフで元の書式に戻す
チェックボックスの作成
チェックボックスにはフォームコントロールとActiveXコントロールの2種類がありますが、違いについての詳細はここでは紹介しません。マクロを割り当てるにはActiveXコントロールを使用します。
フォームコントロールでもボタンだけはマクロの実行を割り当てる事ができます。ボタン以外はセルの値を変化させる事しかできません。VBAの処理を連動させるにはActvieXコントロールを使用しましょう。
開発タブの挿入アイコンをクリックします。
ActiveXコントロールのチェックボックスをクリックします。
適当な場所でドラッグするとチェックボックスが現れます。
デザインモードアイコン→チェックボックスを右クリック→プロパティと進み、現れたプロパティウィンドウのCaption欄を付けたい名前に変更します。
これでチェックボックスの見た目は完成です。先述のフォームコントロールとの違いですが、ActiveXコントロールはプロパティで見た目の変更ができます。
色を付けたい等、書式を変更したいのであればプロパティを適当にさわってください。
ユーザーフォーム内に作成したチェックボックスも同じCheckBoxオブジェクトです。同様に操作できます。
名前の定義でオブジェクトを参照するサンプル
チェックボックスで実行する処理を作成します。名前の定義を利用しているので「VBA 複数範囲 名前の定義」で検索しました。このサイトのサンプルを頂戴します。
名前が月曜で定義されている場合は、Range(“月曜”)で参照できそうです。何やら見慣れぬ記述もありますがとりあえず動かしてみます。
Sub テスト1()
Dim rng As Range
For Each rng In Range("月曜")
MsgBox rng.Value
Next rng
End Sub
定義した範囲が次々とダイアログで表示されます。For Each ~ Nextステートメントの処理によるものです。
For Each ~ Nextステートメント
構文
さっそくOfficeリファレンスを覗いてみます。
For Each…Nextステートメントを使用する
For Each…Nextステートメントは、コレクション内のオブジェクトごと、または配列内の要素ごとに、ステートメントのブロックを繰り返します。ループを実行するたびに、Visual Basicによって変数が自動的に設定されます。
https://docs.microsoft.com/ja-jp/office/vba/language/concepts/getting-started/using-for-eachnext-statements
相変わらず難読です。マーカーで色付けされている部分をサンプルに当てはめるとそれぞれ
- コレクション内のオブジェクトごと、または配列内の要素ごと
Range(“月曜”)
- ステートメントのブロック
MsgBox rng.Value
- 変数
rng
となります。逆にサンプルへ説明文をあてはめるとこうなります。
Sub テスト1()
Dim 変数 As Range
For Each 変数 In コレクション内のオブジェクト
ステートメントのブロック(2行以上あってもOK)
Next 変数
End Sub
処理の流れ
用語と2行目は現時点では無視して下さい。その内覚えます。今回は処理の流れだけ把握しましょう。処理の流れは次の通りです。説明用に月曜の範囲をセルA1,B1,C1とします。
Sub テスト1()
'セル"A1","B1","C1"が「月曜」で[名前の定義]されている
Dim rng As Range
For Each rng In Range("月曜")
MsgBox rng.Value
Next rng
End Sub
5行目/ループ1回目…月曜で定義した範囲の先頭A1セルをrngに代入する
6行目…MsgBox関数を実行する=A1セルに入力された値が表示される
7行目…Next rngで5行目のFor Eachに戻る
5行目/ループ2回目…B1セルをrngに代入する
コレクション内のオブジェクトが無くなるまでこの流れを繰り返します
For Each ~ NextステートメントでForとNextに挟まれている処理を繰り返す訳です。オブジェクトの数だけ繰り返します。これから嫌というほどこのステートメントを使うことになります。今回は記述方法や付帯する用語は無視して、処理の流れをつかみましょう。
マクロに取り組んだのは、こういったエクセル関数や標準機能では実現できない処理をやりたかったからではないでしょうか?それを実感させてくれるステートメントです。
1行目のDim rng As Rangeですが、変数です。VBAを理解する上で非常に重要なポイントになりますが、今回は潔く無視しましょう。
サンプルを編集する
前回記事で作成した書式変更コードをそのままサンプルに入れ込みました。キャンセルするコードも一緒に作成しました。
Sub 月曜日書式変更オン()
Dim rng As Range '変数宣言
For Each rng In Range("月曜") '「月曜」で定義された範囲を取得
With rng.Borders(xlEdgeBottom) '書式変更_下罫線
.color = vbRed
.Weight = xlMedium
End With
With rng.Font '書式変更_太赤字
.color = vbRed
.Bold = True
End With
Next rng '書式変更を取得した範囲まで繰り返す
End Sub
Sub 月曜日書式変更オフ()
Dim rng As Range '変数宣言
For Each rng In Range("月曜") '「月曜」で定義された範囲を取得
rng.Borders(xlEdgeBottom).LineStyle = xlLineStyleNone '罫線を消す
With rng.Font '書式_黒色、太字キャンセル
.color = vbBlack
.Bold = False
End With
Next rng '書式変更を取得した範囲まで繰り返す
End Sub
マクロ月曜日書式変更ONを実行すると、月曜で定義した範囲が全て、書式変更されました。月曜日書式変更OFFを実行すると元の書式に戻りました。
(マクロ実行のショートカットはAlt+F8)
おおよそ狙った動きにはなりましたが、一部想定外の部分があるので修正していきます。
- 日付が入っていない空白部分も下罫線が変更される
- 1セルずつ変更される様子が描画される(見た目が遅そうで嫌)
If~Thenステートメント
日付が入っていない空白部分の対策は、「空白ではない場合処理をしない」で何とかなりそうです。「VBA セルが空白でなかったら」で検索します。
もし〜ならの書き方が If 条件式 Thenなので
If Range(“A1”).Value <> “” Then
https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q10146188485
Ifはエクセル関数にあるので何となく判りますね。ちなみにこのIf~Thenステートメント、1行で記述できる場合はEnd Ifは省略できます。
Application.ScreenUpdating
1セルずつ変更される様子を描画しないように、「VBA 描画を止める」で検索しました。
ScreenUpdatingプロパティで画面の更新を止められるようです。Officeリファレンスも覗いてみましょう。
マクロの速度を向上させるため、画面を更新しないようにします。この場合、マクロの処理過程は見ることができませんが、実行速度が速くなります。マクロが終了した後は、ScreenUpdatingプロパティの値をTrueに戻してください。
https://docs.microsoft.com/ja-jp/office/vba/api/excel.application.screenupdating
Application.ScreenUpdating = True '画面を更新をする
Application.ScreenUpdating = False '画面を更新をしない
先ほどのサイトではこの「Application.ScreenUpdatingを勘違いして乱用しても早くならない」と教えてくれています。
ネットで色々なサンプルを見てきて思ったのですが、おまじないとしてApplication.ScreenUpdatingを書いてる人がたくさんいます。
中には「問答無用の呪文」みたいな紹介をしているところもあったりします。「乱用しても早くならない」はそういう人達へのメッセージでしょう、きっと。
If Thenステートメントといっしょにサンプルへ記述します。
Sub 月曜日書式変更オン()
Dim rng As Range
Application.ScreenUpdating = False '画面の更新を止める
For Each rng In Range("月曜")
If rng.Value <> "" Then 'もし定義した範囲が空白でなかったら
With rng.Borders(xlEdgeBottom)
.color = vbRed
.Weight = xlMedium
End With
With rng.Font
.color = vbRed
.Bold = True
End With
End If
Next rng
Application.ScreenUpdating = True '画面の更新をする
End Sub
Sub 月曜日書式変更オフ()
Dim rng As Range
Application.ScreenUpdating = False
For Each rng In Range("月曜")
rng.Borders(xlEdgeBottom).LineStyle = xlLineStyleNone
With rng.Font
.color = vbBlack
.Bold = False
End With
Next rng
Application.ScreenUpdating = True
End Sub
空白セルは罫線が引かれなくなり、1セルずつ処理される様子も映らなくなりました。
Application.ScreenUpdating = Trueがなくても、プロシージャ終了時に自動でTrueにしてくれます。が、必ず同一プロシージャ内でTrueを設定しましょう(厳密には描画を止める必要がなくなった時点)。「変えたら戻す」のクセを付けておいたほうが吉です。
チェックボックスの判定とマクロの連動
作成したマクロを実行するのにイベントが使えそうです。当然、チェックボックスをトリガー(きっかけ)にしますが、とりあえずSheetモジュールを覗いてみます。
モジュール内にちゃんとCheckBox1がありました。Clickイベントも用意されています。
- チェックボックスON→Sub 月曜日書式変更オン()を実行
- チェックボックスOFF→Sub 月曜日書式変更オフ()を実行
これらをClickイベントで実行するようにします。
「VBA チェックボックス Subプロシージャ実行」でググると以下のサンプルが見つかりました。
Private Sub CheckBox1_Click()
If CheckBox1.Value Then
'Trueのときの処理または、Call 処理1
Else
'Falseのときの処理または、Call 処理2
End If
End Sub
https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q1197144376
CheckBox1.ValueでCheckBoxオブジェクトのValueプロパティを取得しています。ONならTrue、OFFならFalseを返します。
何となくわかるんですけど、これって省略されてね?って事でもうちょっと調べてみると別の書き方がありました。
Private Sub CheckBox1_Click()
If CheckBox1.Value = True Then 'チェックボックスがオンの時
Call 月曜日書式変更オン 'マクロ "月曜日書式変更ON" を実行する
Else 'trueでない場合=falseの場合
Call 月曜日書式変更オフ 'マクロ "月曜日書式変更OFF" を実行する
End If
ActiveCell.Activate 'セルをアクティブにする
End Sub
省略しすぎてもわかりにくいので、わかるように書きました。こっちの方がわかりやすい、と思った人は私と同じ勘違いをしているかも知れません。以下重要です。
最初のネットサンプルの記述が正解です。勘違いサンプルも間違いではないですが冗長です。If Thenステートメントの構文をみてみましょう。
If 条件式(True or False) Then
'Trueのときの処理
Else
'Falseのときの処理
End If
私の勘違いの原因は恐らくエクセルのIF関数です。
エクセル関数の場合、「条件式」が「論理式」になることが多いので、「A = B」や「A < B」のように何らかの比較演算子を使います。その習慣でCheckBox1.Value Thenに違和感を覚え、CheckBox1.Value = True Thenと記述してしまいました。
CheckBox1.Valueはチェックが入っていればTrue、なければFalseを返します(要は条件式に比較演算子が入らない)。勘違いサンプルにあてはめるとそれぞれTrue = True、またはFalse = Trueと記述している事になります。
「冗長でも間違いではないからいい、分かりやすい方がいい」と意固地になる方はそれでも構いませんが、今後VBAを続けていくとこの条件式の中にNotを入れるケースに出会うと思います。冗長記述に慣れている状態で、このNotが入ってくると混乱に輪をかけます。
Private Sub CheckBox1_Click()
If CheckBox1.Value Then 'チェックボックスがオンの時
Call 月曜日書式変更オン 'マクロ "月曜日書式変更ON" を実行する
Else 'チェックボックスがオフの時
Call 月曜日書式変更オフ 'マクロ "月曜日書式変更OFF" を実行する
End If
End Sub
条件式の戻り値がTrue or Falseの場合は= True(False)と記述しないことをおすすめします。
チェックボックスの値を取得、ON/OFFを判定してマクロと連動させるコード
今回の出来高です。
Private Sub CheckBox1_Click()
If CheckBox1.Value Then
Call 月曜日書式変更オン
Else
Call 月曜日書式変更オフ
End If
ActiveCell.Activate
End Sub
Sub 月曜日書式変更オン()
Dim rng As Range
Application.ScreenUpdating = False
For Each rng In Range("月曜")
If rng.Value <> "" Then
With rng.Borders(xlEdgeBottom)
.color = vbRed
.Weight = xlMedium
End With
With rng.Font
.color = vbRed
.Bold = True
End With
End If
Next rng
Application.ScreenUpdating = True
End Sub
Sub 月曜日書式変更オフ()
Dim rng As Range
Application.ScreenUpdating = False
For Each rng In Range("月曜")
rng.Borders(xlEdgeBottom).LineStyle = xlLineStyleNone
With rng.Font
.color = vbBlack
.Bold = False
End With
Next rng
Application.ScreenUpdating = True
End Sub
チェックボックスのオンオフでマクロが実行できるコードが完成しました。
チェックボックスの判定をする上で、2つのステートメント(For Each Nextステートメント、If Thenステートメント)について触れてきました。
これらのステートメントを何となく知っていれば、今後Web上で出会うサンプルもある程度読めると思います。難しい用語は抜きにして、まずはたくさんのサンプルに触れ、動かして、編集するといった作業を積み重ねましょう。各ステートメントの記述方法(文法)は何回も書けば覚えられます。忘れたらまたググればいいだけの話です。
VBA入門で重要なのは、簡単な処理でもいいので「動くマクロを作成すること」です(私見)。知識だけではマクロは動きません。
サンプルにある程度触れた後(=VBAにある程度慣れた後)で以下の2記事を読めばより理解が深まると思います。