[教えて!VBA] 第8回 変更前のセルの値を保持/利用するにはどうすればいいの??
概要
この記事について
かんたんな概要と結論
イベントを利用し、セル入力時に処理を実行させて、変更前の値を取り扱うことができます。
こんにちは、dedeです。
この記事では、
VBAマクロに関する質問のうち、
皆が疑問に思っているトピックについて解説いたします。
今回は、
変更前のセルの値を記録・保持して利用したり、変更値をチェックして入力前の値に戻したりする方法
を解説いたします。
※この記事は、Office VBAマクロのうち
Excel VBAマクロに関するトピックです。
レベル:中級者向け
やりたいこと
シートのセルにデータを入力すると、
通常は変更前の古いデータは消去されます(「元に戻す」ボタンで戻すことはできますが)。
入力したタイミングに応じて、変更前の値をシートのどこかに移したい場合は、
VBAマクロの出番です。
このとき、
イベント
と呼ばれるVBAマクロの仕組みを利用することになります。
また、変更したデータを入力した時に、
適切ではない値だった場合に自動でチェックして前の値に戻したいときもあるでしょう。
そのような各パターンについて見ていきます。
パターン① 変更前の値を他のセルに移したい
ABOUT
入力するたびに、
前の値をシートのどこかのセルに記録していくようなマクロを作成したいという希望があるとしましょう。
「入力するたび」というタイミングの検知のために、
イベントという仕組みを利用します。
イベントとは?
ブックやシートに対して、
ある特定の操作が行われた際に自動でVBAマクロが発火(処理の開始)されるような仕組みです。
例としては次のようなものがあります
対象 | イベント名 | 内容 |
---|---|---|
ブック (WorkBook) |
Open | ファイルを起動して ブックが開かれたときに実行 |
BeforeClose | 閉じるボタンなどでブックが閉じられる時に実行 | |
シート (Worksheet) |
SelectionChange | 現在のセルから別のセルを選択した時に実行 |
Change | セルの内容を変更した時に実行 |
コード
イベントの実装は、
イベントプロシージャというSubプロシージャの記入により行います。
イベントプロシージャは、
イベントを設定したいシートのコードウインドウ上に記入します。
変更前の値を他のセルに移すマクロのコードは下記です。
1Private Sub Worksheet_Change(ByVal Target As Range)
2 Dim OldValue As Variant
3 Dim currentCellAddress
4
5 '//ポイント① 変更前の値を調べたいセル以外では処理を行わないようにする
6 If Target.Address(False, False) <> "B3" Then Exit Sub
7
8 '//ポイント② いったんイベントを無効化し、イベントの発火を停止する
9 Application.EnableEvents = False
10
11 '//カーソル位置を退避させる
12 currentCellAddress = Selection.Address
13
14 '//ポイント③ いったんセルの値をもとに戻し、古い値を変数に退避させてから再度変更後の値に戻す
15 Application.Undo
16 OldValue = Target.Value
17 Application.Undo
18
19 '//変更前の値を別のセルに移す
20 Me.Range("D3").Value = OldValue
21
22 '//カーソル位置を元に戻す
23 Me.Range(currentCellAddress).Select
24
25 '//イベントを再度有効にする
26 Application.EnableEvents = True
27
28End Sub
29
ポイントは三点あり、
①変更前の値を調べたいセル以外では処理を行わないようにするため、Target
(変更したセルのRangeを参照する引数)のアドレスについて絞り込みを行います。
上では、B3セル以外では処理を行わないようにします。
②処理の最中に他のイベントが発火しないように、EnableEvents
を変更しておきます。
③いったんセルの値をもとに戻し、古い値を変数に退避させてから再度変更後の値に戻します。
デモ
変更前に東京都が入っています。
新しいデータを入力後、別のセルに東京都の文字が移されます。
パターン② 変更前の値を他のセルに移し、削除した値を別のセルに移したい
ABOUT
変更ではなく、
削除した場合(空欄として入力した場合)には別のセルに値を移すという仕様にしたいとしましょう。
その場合、いくらか条件分岐が増え、
また、削除したことを示すフラグを変数に持たせる必要が発生します。
コード
1Private Sub Worksheet_Change(ByVal Target As Range)
2 Dim inputValue As Variant
3 Dim OldValues(0 To 1) As Variant
4 Dim currentCellAddress
5
6 '//変更前の値を調べたいセル以外では処理を行わないようにする
7 If Target.Address(False, False) <> "B3" Then Exit Sub
8
9 '//いったんイベントを無効化し、イベントの発火を停止する
10 Application.EnableEvents = False
11
12 '//カーソル位置を退避させる
13 currentCellAddress = Selection.Address
14
15 '//いったんセルの値をもとに戻し、古い値を変数に退避させてから再度変更後の値に戻す
16 inputValue = Target.Value '//入力した値を変数に退避
17 Application.Undo
18 '//ポイント① 入力した値が空欄であるかどうかで削除処理か変更処理かを分ける
19 If inputValue = "" Then
20 OldValues(0) = "delete"
21 Else
22 OldValues(0) = "change"
23 End If
24 OldValues(1) = Target.Value
25 Application.Undo
26
27 '//変更前の値を別のセルに移す
28 '//ポイント② 削除処理であればF3セルに、変更処理であればD3セルに移す
29 If OldValues(0) = "delete" Then
30 Me.Range("F3").Value = OldValues(1)
31 Else
32 If OldValues(1) <> "" Then Me.Range("D3").Value = OldValues(1)
33
34 End If
35
36 '//カーソル位置を元に戻す
37 Me.Range(currentCellAddress).Select
38
39 '//イベントを再度有効にする
40 Application.EnableEvents = True
41
42End Sub
ポイントは、
①入力した値が空欄であるかどうかで、
削除であるか変更であるか判別できるようなフラグを持っておきます。
②削除処理、変更処理ごとに反映するセルを分けています。
デモ
県名を変更すると、
「前回の入力値」欄に変更前の値が入ります。
また、データ削除すると、
「削除された値」欄に削除前のデータが入ります。
前回の入力値欄のデータには変更がありません。
パターン③ 変更時に値をチェックし、不正な値であれば入力前に戻したい
ABOUT
イベントの仕組みを使って、
入力した際に即座に入力値のチェックをして不正な値を弾きたい時の実装を記します。
コード
1
2Private Const HOKURIKU As String = "富山;石川;福井;"
3
4Private Sub Worksheet_Change(ByVal Target As Range)
5 Dim OldValue As Variant
6 Dim currentCellAddress
7 Dim HOKURIKUs() As String
8 Dim oneHokuriku As Variant
9 Dim isHokuriku As Boolean
10
11 '//変更前の値を調べたいセル以外では処理を行わないようにする
12 If Target.Address(False, False) <> "B3" Then Exit Sub
13
14 '//いったんイベントを無効化し、イベントの発火を停止する
15 Application.EnableEvents = False
16
17 '//カーソル位置を退避させる
18 currentCellAddress = Selection.Address
19
20 '//北陸県を配列として取得する
21 HOKURIKUs = Split(HOKURIKU, ";")
22 isHokuriku = False
23
24 '//ポイント① 入力値が北陸県のいずれかに該当すれば戻さない
25 '// 該当しなければ変更前に戻すようにする
26 For Each oneHokuriku In HOKURIKUs
27 If Target.Value = oneHokuriku Then
28 isHokuriku = True
29 Exit For
30 End If
31 Next oneHokuriku
32 If Not isHokuriku Then Application.Undo
33
34 '//カーソル位置を元に戻す
35 Me.Range(currentCellAddress).Select
36
37 '//イベントを再度有効にする
38 Application.EnableEvents = True
39
40End Sub
ポイントとしては、
入力値チェックの機構を導入しています。
入力値が北陸県(富山;石川;福井)のいずれかに該当すればそのまま入力を続行しますが、
もし北陸県以外の文字列が入力されれば、変更前に戻します。
デモ
入力したときに北陸県以外の文字であれば、
空欄に戻されます。
北陸県のどれかであれば、
チェックが通り、通常通り入力が完了できます。
パターン④ 変更した値を複数保持し、リストとして他のセルに出力したい
ABOUT
変更前の値として複数持てるようにしたいという希望があるとします。
次のように、一つのセルに改行で分けられたリストの形で
最大10個の値を格納していきます。
コード
1Private Sub Worksheet_Change(ByVal Target As Range)
2 Dim OldValue As Variant
3 Dim currentCellAddress
4 Dim lists() As String
5 Dim firstCount As Long
6 Dim valueFromList As Variant
7 Dim i As Long
8
9 '//変更前の値を調べたいセル以外では処理を行わないようにする
10 If Target.Address(False, False) <> "B3" Then Exit Sub
11
12 '//いったんイベントを無効化し、イベントの発火を停止する
13 Application.EnableEvents = False
14
15 '//カーソル位置を退避させる
16 currentCellAddress = Selection.Address
17
18 '//いったんセルの値をもとに戻し、古い値を変数に退避させてから再度変更後の値に戻す
19 Application.Undo
20 OldValue = Target.Value
21 Application.Undo
22
23 '//変更前の値を別のセルに移す
24 '////配列の形で現在のリストを取得する
25 lists = Split(Me.Range("D3").Value, vbLf)
26 firstCount = LBound(lists)
27
28 '////リスト項目が10以上であれば、最初の値を消すようにする
29 If (UBound(lists) - LBound(lists) + 1) = 10 Then firstCount = firstCount + 1
30
31 '////新しいリストを作成する
32 For i = firstCount To UBound(lists)
33 valueFromList = valueFromList & lists(i) & vbLf
34 Next i
35 valueFromList = valueFromList & OldValue
36
37 Me.Range("D3").Value = valueFromList
38
39 '//カーソル位置を元に戻す
40 Me.Range(currentCellAddress).Select
41
42 '//イベントを再度有効にする
43 Application.EnableEvents = True
44
45End Sub
46
47
改行コードでリストを分割して配列として取り扱い、
再度格納する際には分割したそれぞれを改行コードで結合します。
デモ
これまでにデータ1~10を入力済みで、
現在の値がデータ11である状態です。
あたらしく「データ12」を入力すると、
最も古い値が消去され、
データ2~データ11がリストに格納されています。
終わりに
イベントを使いこなすことで、
Excel単独でも高度なアプリケーションのように
さまざまな機能をもたせることができます。
イベントの中でも、
Change
イベントはよく使われるので、
これを理解するとマクロが上達するでしょう。
関連記事
- [教えて!VBA] 第7回 フォルダを開く(+ファイルを操作する)にはどうすればいいの??
- [教えて!VBA] 第6回 エクセルVBAマクロで、マクロを終了するにはどうすればいいの??
- [教えて!VBA] 第5回 ブックの最初のシートの最初のセルを選択した状態にするにはどうすればいいの??
- [教えて!VBA] 第4回 シート上の図形をコピーして他のセル上に貼り付けするにはどうすればいいの??
- [Excel VBA] セルの文字色・背景色だけをコピーして貼り付けるマクロを作成しました