1. はじめに
Excel VBAは導入しやすく、実務でも使用しやすい言語です。
そして比較的ゆるい言語でもあります。
その「ゆるさ」は、
画面を作ってすぐに動かせるという意味では優しさですが、
実務では 意図しない状態が残る不具合を生みやすい原因にもなります。
本記事では、
VBAでユーザーフォームを使った際に起きがちな
「なぜか前回の値が残っている」問題について整理します。
2. ユーザーフォームで起きる典型的な現象
ユーザーフォームは使用者に入力値・形式、入力箇所の制限をかける便利な機能です。
しかし、時に落とし穴があります。
次のような経験はないでしょうか。
-
フォームを閉じたのに、次に開くと前回の入力値が残っている
-
初期化したつもりなのに、条件によっては値が残る
-
一度正常に動いた後、再実行すると挙動が変わる
これらはすべて、
ユーザーフォームが「状態を保持するオブジェクト」 であることに起因します。
3. なぜ値が残るのか
ユーザーフォームは、表示している間だけ存在する画面ではありません。
実際には、メモリ上に生成されたオブジェクトです。
そして、次のように扱われます。
-
Unloadされない限り、オブジェクトは破棄されない -
破棄されない限り、コントロールの値も保持される
4. よくある原因① Hide と Unload の違い
-
Hideの場合
UserForm1.Hide
フォームは非表示になるだけで、オブジェクトは メモリに残ります。
(TextBox の値もそのまま)
-
Unloadの場合
Unload UserForm1
フォームオブジェクトが破棄され、次回表示時は 新規生成になります。
よくある勘違いで「閉じた=初期化された」
これは 誤りです。
5. よくある原因② 初期化コードの位置が不適切
次のようなコードもよく見かけます。
Private Sub CommandButton1_Click()
TextBox1.Value = ""
Me.Hide
End Sub
この場合、
正常終了ルートでは初期化されますが、
- 別のボタン
- フォーム右上の ×
- エラー停止
では 初期化されません。
結果として、実行経路によってフォームの状態が変わるコードになります。
6. 問題の本質:フォームも「状態を持つ」
ここで重要なのは、ユーザーフォームもグローバル変数と同じく
状態を保持する存在だという点です。
-
前回の入力
-
前回の処理結果
-
エラーで止まった途中状態
これらが次回実行に影響します。
7. 実行のたびに結果が変わる理由
ユーザーフォームを使った処理では、
- どの経路で閉じたか
- フォームを
Unloadしたか -
Hideしただけか
によって、同じ操作でも結果が変わる可能性があります。
これは、
- グローバル変数
-
Find(検索状態) -
AutoFilter(フィルター状態)
と 同じ構造の問題です。
8. 安全な設計① フォームは必ず Unload する
基本方針はシンプルです。
使い終わったフォームは必ず Unload する
Unload Me
これだけで、状態の持ち越し、前回値の残留の多くは防げます。
9. 安全な設計② 初期化は UserForm_Initialize に集約する
初期値設定は、必ずここで行います。
Private Sub UserForm_Initialize()
TextBox1.Value = ""
CheckBox1.Value = False
End Sub
-
表示経路に依存しない
-
毎回確実に実行される
という利点があります。
10. 安全な設計③ 状態を外に持ち出さない
フォーム内部の値を、
-
グローバル変数に入れる
-
別フォームが直接参照する
設計は、状態地獄の入口です。
必要な値は、
-
明示的に取得
-
引数や戻り値で受け渡す
ようにします。
11. まとめ
-
ユーザーフォームはオブジェクト
-
Hideでは状態は消えない -
Unloadしない限り値は残る -
初期化位置が重要
-
実行経路で結果が変わる
結論として、
ユーザーフォームも「状態を持つもの」として扱う
これを意識するだけで、フォーム起因の不具合は大きく減ります。