🚀The world's best VBA AI has evolved. ExcelMaster is now an autonomous Agent.Read more →
Back to Blog

Excel VBA の Worksheet_Activate と Deactivate — シートを切り替えたときにコードを走らせる(そして離脱を取り消せない理由)

|

Excel VBA の Worksheet_Activate と Deactivate — シートを切り替えたときにコードを走らせる(そして離脱を取り消せない理由)

TL;DRWorksheet_Activate は、シートがアクティブになったときに発火します (あなたがそこへ着地する)。Worksheet_Deactivate は、あなたがそのシートを離れる ちょうどそのとき(別のシートが引き継ぐ前)に発火します。これらはシートにとっての 到着と出発のイベントです — Activate で表示時にダッシュボードをリフレッシュし、 Deactivate でユーザーが先へ進む前に物事を確かめます。両者を定義づける勘所: どちらにも Cancel 引数がありません。 BeforeCloseBeforeSave と違い、シートの切り替えを見ることはできても、ブロックはできません — できる 最善は、ユーザーを押し戻すことです。

' シート自身のモジュール(例:"Dashboard")に置く — ThisWorkbook でも Module でもない。
Private Sub Worksheet_Activate()
    Me.Range("A1").Select              ' 到着するたびに先頭へ移動する
    ActiveWindow.ScrollRow = 1
    Me.Calculate                       ' 表示時にこのシートの数値をリフレッシュする
End Sub

Private Sub Worksheet_Deactivate()
    ' ここには Cancel パラメーターが存在しない — 警告はできても、離脱は止められない。
    If Me.Range("Signoff").Value = "" Then _
        MsgBox "Heads up: you left the Dashboard without signing off.", vbInformation
End Sub

シートを切り替えることは、ユーザーがするいちばんありふれた行為であり、この 2 つの イベントは、シートがそれに反応することを可能にします。Worksheet_Activate は 「このタブを見たらリフレッシュする」を支えます — 再計算、再クエリ、先頭へスクロール、 保護・保護解除。Worksheet_Deactivate は「立ち去る前に確かめる」を支えます。コツは、 これらのイベントにできないことを理解することです。それこそが、これらを門番イベント から分けるものだからです。

この記事で学べること

  • 考え方の軸 — 到着と出発のイベント、シートごとに 1 組
  • Cancel がない理由 — そしてユーザーを引き止めたいときの押し戻しの回避策
  • 表示時にリフレッシュするパターン(そして「開いたときに走らなかった」落とし穴)
  • シートレベルの Worksheet_Activate 対ブックレベルの Workbook_SheetActivate
  • グラフシートと EnableEvents のガードが、ここで両方とも効いてくる理由

考え方の軸:到着と出発のゲート

各シートを、空港のゲートだと思い浮かべてください。Worksheet_Activate は、あなたが このゲートに歩み入った瞬間に発火します — シートがいまアクティブになったのです。 Worksheet_Deactivate は、あなたが歩み出るときに発火します — 別のタブをクリック し、このシートが引き継ごうとしているのです。どちらもあなたが呼ぶことはありません。 Excel が切り替えのときに、順に発火させます:まず古いシートの Deactivate、それから 新しいシートの Activate です。

両方とも引数なしで、両方ともその特定のシートのコードモジュールに置かれます — ThisWorkbook でも、標準の Module でもありません。「Microsoft Excel Objects」の下で シート(たとえば Dashboard)をダブルクリックし、そこに Private Sub Worksheet_Activate() を書きます。中では Me がそのシートを指すので、 Me.CalculateMe.Range(...) がすっきり読めます。

これらのイベントを定義づけるルール:拒否権はない

これが、このクラスター全体を束ねる洞察です。BeforeCloseBeforeSave は、それぞれ Cancel フラグを手渡します — ハンドラーは、その動作を止められるチェックポイント です。Worksheet_ActivateWorksheet_Deactivate には、そのようなパラメーターが ありません。 シグネチャを見てください:Worksheet_Deactivate() は何も取りません。 設定すべき Cancel がないので、ユーザーがシートを離れるのを防ぐ方法はありません。 Deactivate が走るときには、切り替えの決定はすでに下されています。

これは見落としではありません — シートの切り替えは、閉じたり保存したりするような、 リスクのある取り返しのつかない行為ではないので、Excel は拒否権を差し出しません。 けれどそれは、「B2 が埋まるまでこのタブを離れさせない」が直接には不可能だという ことを意味します。正直な回避策は押し戻しです:問題を検出してシートを再アクティブ にし、再発火からガードします:

Private Sub Worksheet_Deactivate()
    If Me.Range("B2").Value = "" Then
        Application.EnableEvents = False    ' 下の Activate が連鎖しないように
        Me.Activate                         ' ユーザーをこのシートへ引き戻す
        Application.EnableEvents = True
        MsgBox "Fill in B2 before leaving this sheet.", vbExclamation
    End If
End Sub

これが何であるかを理解してください:キャンセルされた切り替えではなく、完了した 切り替えの直後に続く、戻る切り替えです。ちらつき、本物の拒否権より重く、イベントを 再発火させます — だから Application.EnableEvents のガードがあります。これは Worksheet_Change で使うのと同じ規律です。データが 正しくなるまで本当にユーザーを引き止める必要があるなら、シートイベントと戦うより、 モーダルで表示した UserForm のほうがきれいな道具です。限界を知れば、存在しない Cancel を追いかける代わりに、その周りを設計できます。

Activate を値打ちにするパターン:表示時にリフレッシュ

Worksheet_Activate の最良の使い道は、「誰かが見た瞬間に、このシートを正しくする」 です。サマリータブが再計算し、レポートがソースを再クエリし、データ入力シートが先頭へ スクロールして入力セルの保護を解除します:

Private Sub Worksheet_Activate()
    Me.Unprotect Password:="x"
    Me.Range("Inputs").Locked = False
    Me.Calculate
    Application.Goto Me.Range("A1"), Scroll:=True   ' 到着時に A1 を左上に置く
End Sub

「Activate が走らなかった」という質問を絶えず生む落とし穴が 1 つあります: ブックが開いたときにすでにアクティブなシートは、Worksheet_Activate を発火させ ません — そこへ切り替えられたのではなく、単に開始シートだったからです。その最初の シートで開いた時点でリフレッシュを起こす必要があるなら、Workbook_Open からも走らせるか、リフレッシュルーチンを両方から呼びましょう。

正しく捉える区別:シートレベル対ブックレベル

シートモジュールの中の Worksheet_Activate は、その 1 枚のシートに対してだけ発火 します。ユーザーがどのシートに着地しても反応したいなら、同じハンドラーを 12 枚の シートモジュールに貼り付けるのはやめて — ThisWorkbook のブックレベルのイベントを 使います:

' ThisWorkbook に置く — すべてのシートで発火し、どのシートかを Sh で教える。
Private Sub Workbook_SheetActivate(ByVal Sh As Object)
    Application.StatusBar = "You are on: " & Sh.Name
End Sub

Private Sub Workbook_SheetDeactivate(ByVal Sh As Object)
    ' Sh は離れようとしているシート。
End Sub

ブック版は Sh — 関わっているシート — を手渡すので、1 つのハンドラーがファイル全体を カバーします。ルールはこうです:特定の 1 枚のシート → そのシートの Worksheet_Activate。どのシートでも → ThisWorkbookWorkbook_SheetActivate ブックレベルのつもりでシートレベルを選ぶ(またはその逆)ことが、「イベントが間違った タブで発火する」いちばんよくある理由です。

もう 2 つ、知っておく価値のある境目。Worksheet_Activateワークシート専用です — グラフシートを選ぶと、このイベントではなく Chart_Activate が発生するので、これを 期待するハンドラーは無言のまま決して走りません。そして、これらのハンドラーの中でする どんな書き込みや選択も、さらなるイベントへ連鎖しかねません。軽く保ち、ブックを変える ときは Application.EnableEvents に手を伸ばしましょう。

ExcelMaster の活用

シート切り替えイベントは単純に見えて、本物の判断を隠しています:どのイベントか(到着 対出発)、どのレベルか(シート対ブック)、離脱をキャンセルできないという事実、開いた ときの最初のシートの落とし穴、そしてどんな押し戻しにも EnableEvents のガード。

ExcelMaster なら、代わりに結果を述べられます。「Dashboard タブを開くたびに、再計算して先頭へ スクロールして」と言えば、正しいシートに Worksheet_Activate を書きます — そして 「サインオフせずに離れたら警告して」と頼めば、ブロックできるふりをするのではなく 警告する Worksheet_Deactivate を足します。できないからです。ブックもコードもあなたの もの。限界を痛い目で学ぶ部分だけを飛ばせるのです。

よくある質問

Worksheet_Activate と Worksheet_Deactivate の違いは何ですか?

Worksheet_Activate は、シートがアクティブになったときに走ります — いまそこへ切り 替えたのです。Worksheet_Deactivate は、そのシートを離れるとき、別のシートが アクティブになる直前に走ります。切り替えのとき、Excel はまず古いシートの Deactivate、 それから新しいシートの Activate を発火させます。両方とも特定のシートのコード モジュールに置かれ、引数を取りません。

VBA でユーザーがワークシートを離れるのを止められますか?

直接にはできません — Worksheet_Deactivate には Cancel パラメーターがないので、 Workbook_BeforeClose が閉じる操作を拒否するようには、切り替えを拒否できません。 回避策は押し戻しです:Deactivate の中で Me.Activate を呼んでユーザーをシートへ 戻し(Application.EnableEvents = False … = True でガードする)、それからメッセージを 出します。本物のブロックには、代わりにモーダルの UserForm を表示しましょう。

ブックを開いたとき Worksheet_Activate が走らないのはなぜですか?

開始シートはそこへ切り替えられたのではなく — ファイルが開いたときにすでにアクティブ だったので、そのシートにはアクティブ化イベントが発火しないからです。別のシートへ移って 戻れば発火しますが、開いた時点での最初のシートには、リフレッシュを Workbook_Open からも走らせましょう。

1 枚だけでなく、どのシートが選ばれてもコードを走らせるには?

ThisWorkbook のブックレベルのイベントを使います: Private Sub Workbook_SheetActivate(ByVal Sh As Object) は、すべてのシートで発火し、 アクティブになったシートを Sh として渡します。Worksheet_Activate をすべての シートモジュールにコピーするより、これのほうがきれいです。

Worksheet_Activate のコードはどこに書きますか?

特定のワークシートのコードモジュールの中です — プロジェクトエクスプローラーの 「Microsoft Excel Objects」の下でそのシート(例:Sheet1Dashboard)をダブル クリックし、Private Sub Worksheet_Activate() を追加します。標準の Module からは 発火しません。すべてのシート版の Workbook_SheetActivateThisWorkbook に置きます。

検証環境

検証環境: Excel 365 (Windows 11), VBA 7.1 — 最終確認 2026-08-05。

関連ガイド: VBA Workbook_BeforeClose · VBA Workbook_BeforeSave · VBA Workbook_Open · VBA Worksheet_SelectionChange · VBA On Error