Article

【Excel VBA】セルを点滅させるマクロ|停止罠と複数セル対応

この記事で分かること

Excel でセルを点滅させる VBA マクロを、コピペで動く最小コード・停止罠・複数セル個別色復元の 3 セットで実機検証つき解説します。

Excel で「期限切れ」「入力エラー」「在庫切れ」などのセルを点滅させて目立たせたいことがあります。条件付き書式単独では時間で点滅させることができず(Excel は時間経過だけでは NOW() を再評価しません)、時間ベースの点滅を実用的に実現する手段としては VBA の Application.OnTime がほぼ唯一の選択肢です。本記事ではコピペで動く最小コードから、停止し忘れない書き方、複数セルを個別に点滅させる方法、別ブックを開いた時に壊れない書き方、そして「条件付き書式では点滅できない理由」までを Excel 2024 で実機検証しながら整理します。

サンプルファイル(xlsm)

本記事のコード A・B・C をそのまま埋め込み、使い方・1 セル点滅・複数セル点滅・条件付き書式比較・コードの 5 シートを同梱したマクロ有効ブックです。

xlsm 形式 ・ シート 5 枚 ・ Excel 2024 build 19929 で動作確認。

excel-vba-blink-cell-sample.xlsm

コピペで動く最小コード(コード A)

まずは Sheet1B2 セルを 1 秒間隔で赤と透明の往復で点滅させる、最小限のマクロを示します。標準モジュールに貼り、StartBlinking を実行すれば点滅が始まり、StopBlinking で止まります。準備(.xlsm 保存・開発タブの表示・モジュール追加)の手順は次の H2 にまとめました。

Option Explicit

' 点滅させたいシート名とセル番地(変更時はこの 2 行だけ書き換える)
Public Const BlinkSheetName As String = "Sheet1"
Public Const BlinkAddress   As String = "B2"

' OnTime キャンセル用に予約時刻を Public 変数で保存(引数完全一致罠の回避)
Public g_NextBlink   As Double
Public g_BlinkActive As Boolean

' 点滅開始(マクロボタンに割り当てる)
Public Sub StartBlinking()
    If g_BlinkActive Then Exit Sub          ' 連打ガード
    g_BlinkActive = True
    BlinkTick
End Sub

' 1 秒ごとに呼ばれる本体
Public Sub BlinkTick()
    If Not g_BlinkActive Then Exit Sub

    With ThisWorkbook.Worksheets(BlinkSheetName).Range(BlinkAddress)
        If .Interior.ColorIndex = 3 Then    ' 赤
            .Interior.ColorIndex = xlNone   ' 透明
        Else
            .Interior.ColorIndex = 3        ' 赤
        End If
    End With

    g_NextBlink = Now + TimeSerial(0, 0, 1)
    Application.OnTime g_NextBlink, _
        "'" & ThisWorkbook.Name & "'!BlinkTick"
End Sub

' 点滅停止(マクロボタンに割り当てる)
Public Sub StopBlinking()
    g_BlinkActive = False                    ' 次のコールバックを空振り化

    On Error Resume Next
    Application.OnTime g_NextBlink, _
        "'" & ThisWorkbook.Name & "'!BlinkTick", , False
    On Error GoTo 0

    ThisWorkbook.Worksheets(BlinkSheetName).Range(BlinkAddress) _
        .Interior.ColorIndex = xlNone
End Sub

Microsoft 公式のサンプルコード「セルを点滅させる」(2023-04-07 更新)は、基本骨格として参考になります。ただし、本記事のコード A と比べると、次の 2 行は実用上の課題になります。Application.Goto Range("A1"), 1 は毎秒アクティブセルを A1 に飛ばすため、ユーザーの選択操作を奪います。Range(BlinkCell).Interior.ColorIndex = 0 の代入値 0 は、公式の XlColorIndex 列挙体に名前を持ちません。Excel 2024 build 19929 では、代入後の読み戻しが -4142xlColorIndexNone)に正規化されます。上記のコード A では Application.Goto を取り除き、xlNone を直接代入しています。さらに Application.OnTimeRange の両方にブックとシートの修飾を入れました。修飾なしの公式コードをそのまま使うと、別のブックをアクティブにした瞬間にコールバックが発火しなくなったり、別ブック側のセルを赤くしてしまったりする現象が再現できました(詳しくは「うまく動かないときのチェック項目」で扱います)。

VBE で標準モジュールに貼り付けたコード A の全体像
標準モジュール BlinkOneCell に貼ったコード A。StartBlinking をマクロ実行すれば B2 セルが 1 秒ごとに点滅する。

準備|.xlsm 保存・開発タブ・色定数の落とし穴

コード A を動かすために必要なのは次の 4 つです。どれか 1 つでも欠けると「マクロが書けない」「保存しても次回開いたら消えている」といった事故になります。

  • 開発タブを表示する:リボン上で右クリック → 「リボンのユーザー設定」→ 右側の一覧で「開発」にチェック → OK。
  • VBE(Visual Basic Editor)を開く:開発タブの「Visual Basic」ボタン、または Alt + F11
  • 標準モジュールを追加する:VBE のプロジェクトエクスプローラーで対象ブックを右クリック → 「挿入」→ 「標準モジュール」。本記事のサンプルでは追加したモジュール名を BlinkOneCell に変更しています。
  • .xlsm 形式で保存する:「ファイル」→「名前を付けて保存」→ ファイルの種類で「Excel マクロ有効ブック(*.xlsm)」を選ぶ。.xlsx で保存すると VBA モジュールが警告付きで削除されます。
.xlsx で上書き保存しようとしたときに表示される VBA 削除の警告ダイアログ
.xlsx で保存すると「次の機能はマクロなしのブックに保存できません」と警告が出る。「いいえ」を選んで .xlsm で保存し直す。

実機検証でも、.xlsx(FileFormat = 51)で SaveAs したファイルを開き直すと標準モジュールが消えており、.xlsm(FileFormat = 52)では同じコードがそのまま残ることを確認しました。.xlsx は「マクロを保存しない」形式と覚えておくのが安全です。

色の指定にも注意点があります。Interior.ColorIndex = 3 は標準の赤、Interior.ColorIndex = xlNone(値は -4142)は塗りつぶしなしを意味します。一方、公式コードに出てくる Interior.ColorIndex = 0 は、XlColorIndex 列挙体には名前のない値で、Excel 2024 build 19929 で代入してから読み戻すと -4142 に正規化されました。動作上の見た目は同じです。コードを読み返す人を意識すると、xlNone(または xlColorIndexNone)の方が意図は伝わりやすくなります。自動色を指定したい場面では xlColorIndexAutomatic(値は -4105)が公式の定数です。

停止できない原因と「引数完全一致罠」

点滅マクロでもっとも多いのは、「StopBlinking を呼んでも止まらない」「実行時エラー 1004 になる」という問い合わせです。Application.OnTime の予約を取り消すには、第 4 引数 ScheduleFalse にした上で、予約時の引数(EarliestTimeProcedure)と完全に一致する値を渡す必要があります。

Microsoft 公式の Application.OnTime メソッド(2025-11-13 更新)では次のように書かれています。

「スケジュール」を「False」に設定すると、以前に同じプロシージャと EarliestTime 値で設定された プロシージャがクリアされます。

つまり、毎回新しく計算した Now + TimeSerial(0, 0, 1) をその場で渡してキャンセルしようとしても、予約時の時刻と一致しないので消えません。Excel 2024 build 19929 でも実機で再現し、引数を一致させずに Schedule:=False を呼ぶと 1004: メソッド 'OnTime' を実行できませんでした エラーになりました。

Application.OnTime の引数不一致でキャンセル失敗時に表示される実行時エラー 1004 ダイアログ
引数を完全一致させずに Schedule:=False を呼んだ場合の実行時エラー 1004。

コード A では、予約時刻を g_NextBlink As Double という Public 変数で保持し、StopBlinking 側でも同じ変数を渡すことで一致を保証しています。プロシージャ名も "'" & ThisWorkbook.Name & "'!BlinkTick" の形でブック修飾しているので、予約と取り消しの両方が同じ文字列で行われます。

もう 1 つ覚えておきたいのが、g_BlinkActive フラグです。OnTime の予約は厳密なリアルタイムタイマーではありません。Excel 側で他の処理を実行中のときは、発火タイミングが遅れます。公式の Application.OnTime メソッドページLatestTime パラメータ説明には、次の記載があります。

別のプロシージャが実行されているために、Microsoft Excel が Ready、Copy、Cut、または Find モードでない場合、Excel は最初のプロシージャが完了するまで 30 秒待ちます。

引用文の「30 秒」は、LatestTimeEarliestTime + 30 秒に指定したときの例です。本記事のコード A は LatestTime を省略しているので、Excel は実行可能になるまで待機し続けます(30 秒で打ち切られるわけではありません)。いずれにせよ、Excel が編集途中・コピー・切り取り・検索のいずれかのモードにある間は、OnTime のコールバックは秒単位で遅延します。そのため Schedule:=False で予約自体は消したつもりでも、すでにキューに入った 1 回分のコールバックは残ることがあります。g_BlinkActive = False を立てておけば、最後のコールバックが届いても If Not g_BlinkActive Then Exit Sub で空振りするので、止め損ねを二重で防げます。

複数セルを個別の色で点滅させる

次に Sheet1!A1:A5 を一括で点滅させたいケースを考えます。最初に思い付くのは、Range("A1:A5").Interior.Color = ... を 1 秒ごとに切り替える方法でしょう。ただしこれだと「全部赤」と「全部透明」を往復するだけです。元のセルの色(黄・青・緑・紫・橙のような個別の塗り)は失われます。点滅を止めた後に元の色を戻したいときに困ります。

そこで、開始時に Dictionary へ各セルのアドレスと元の色(Interior.Color)を保存します。Interior.ColorIndex も併せて保持しておきます。点滅の往復では「赤」と「元の色」を切り替えます。ColorIndex を併せて持っておくと、元が「塗りつぶしなし(xlNone)」のセルでも、停止後にそのまま塗りつぶしなしへ正しく復元できます。Interior.Color だけ保存していると、塗りつぶしなしの白セルが停止後に「白塗り」になる事故が起きます。コード B(複数セル個別色復元版)は、コード A とは別の標準モジュール(例:BlinkMultiCell)に貼ってください。同じ標準モジュールに A の下にそのまま貼ると、Option Explicitg_NextBlink / g_BlinkActive が重複してコンパイルエラーになります。1 セル版だけ使う場合は、コード A は貼らずにコード B だけを貼ります。

Option Explicit

' 点滅対象範囲(シート名とアドレスを分けて保持)
Public Const BlinkRangeSheet   As String = "Sheet1"
Public Const BlinkRangeAddress As String = "A1:A5"

Public g_NextBlink            As Double
Public g_BlinkActive          As Boolean
Public g_OriginalColors       As Object   ' Dictionary: Address → Interior.Color
Public g_OriginalColorIndices As Object   ' Dictionary: Address → Interior.ColorIndex

Public Sub StartBlinkingMulti()
    If g_BlinkActive Then Exit Sub          ' 連打ガード
    Set g_OriginalColors       = CreateObject("Scripting.Dictionary")
    Set g_OriginalColorIndices = CreateObject("Scripting.Dictionary")

    Dim rng As Range
    Set rng = ThisWorkbook.Worksheets(BlinkRangeSheet).Range(BlinkRangeAddress)

    Dim cell As Range
    For Each cell In rng.Cells
        g_OriginalColors(cell.Address)       = cell.Interior.Color
        g_OriginalColorIndices(cell.Address) = cell.Interior.ColorIndex
    Next cell

    g_BlinkActive = True
    BlinkTickMulti
End Sub

Public Sub BlinkTickMulti()
    If Not g_BlinkActive Then Exit Sub

    Dim rng As Range
    Set rng = ThisWorkbook.Worksheets(BlinkRangeSheet).Range(BlinkRangeAddress)

    Dim cell As Range
    For Each cell In rng.Cells
        If cell.Interior.ColorIndex = 3 Then
            ' 赤 → 元の色(元が塗りつぶしなしなら xlNone で戻す)
            If g_OriginalColorIndices(cell.Address) = xlNone Then
                cell.Interior.ColorIndex = xlNone
            Else
                cell.Interior.Color = g_OriginalColors(cell.Address)
            End If
        Else
            cell.Interior.ColorIndex = 3        ' 赤
        End If
    Next cell

    g_NextBlink = Now + TimeSerial(0, 0, 1)
    Application.OnTime g_NextBlink, _
        "'" & ThisWorkbook.Name & "'!BlinkTickMulti"
End Sub

Public Sub StopBlinkingMulti()
    g_BlinkActive = False

    On Error Resume Next
    Application.OnTime g_NextBlink, _
        "'" & ThisWorkbook.Name & "'!BlinkTickMulti", , False
    On Error GoTo 0

    If Not g_OriginalColorIndices Is Nothing Then
        Dim rng As Range
        Set rng = ThisWorkbook.Worksheets(BlinkRangeSheet).Range(BlinkRangeAddress)

        Dim cell As Range
        For Each cell In rng.Cells
            If g_OriginalColorIndices.Exists(cell.Address) Then
                If g_OriginalColorIndices(cell.Address) = xlNone Then
                    cell.Interior.ColorIndex = xlNone
                Else
                    cell.Interior.Color = g_OriginalColors(cell.Address)
                End If
            End If
        Next cell
    End If
End Sub
複数セル点滅マクロ実行前と停止後で各セルの元の色(黄・青・緑・紫・橙)が復元されている比較
点滅開始前の元の色(黄・青・緑・紫・橙)が、StopBlinkingMulti 後にそのまま復元される。

もう 1 点、複数セルを書き換えるマクロを実行した後は、Ctrl + Z で取り消せる履歴は安定しないことがあります。標準の Excel 操作と違って、VBA から色を変更した記録は元に戻すスタックに残らないことがあるためです。本記事のコードは StopBlinking / StopBlinkingMulti 自体に「元の色を戻す」処理を入れているので、停止マクロから戻す運用にすると確実です。

うまく動かないときのチェック項目

コード A / B をそのまま貼っても点滅しない、あるいは別の場所が点滅してしまうときの典型例を 4 つに分けて整理します。

別のブックをアクティブにすると壊れる|ブック修飾の必要性

Microsoft 公式コードのように Application.OnTime g_NextBlink, "BlinkTick"(ブック名なし)と書き、本体内も Range("Sheet1!B2")(ブック修飾なし)にしているとします。この状態では、別のブックをアクティブにした瞬間に挙動が壊れます。実機で検証したところ、未修飾のコードでは別ブックがアクティブな間はコールバックが発火しませんでした。さらに、OnTime だけブック修飾し、Range の修飾を忘れたケースも検証しました。コールバックは予定どおりマクロブック側で発火するのに、Range("Sheet1!B2") がアクティブブック側の B2 を赤くしてしまう事故が再現できました。

未修飾コードで別ブック B2 が誤って赤く塗られた比較スクリーンショット
未修飾の Range("Sheet1!B2") を使ったマクロは、別ブックがアクティブだとそちらの B2 セルを赤くしてしまう。

本記事のコード A / B はこの問題を、2 か所の完全修飾で防いでいます。Application.OnTime の Procedure 引数は "'" & ThisWorkbook.Name & "'!BlinkTick" の形でブック修飾します。セル参照は ThisWorkbook.Worksheets(BlinkSheetName).Range(BlinkAddress) としてブックとシートを完全修飾します。マクロを書き換えるときは、この 2 か所を未修飾に戻さないようにしてください。

シート保護中は実行時エラー 1004 が出る

校閲タブの「シートの保護」を有効にしているシートで Interior.ColorIndex を変更しようとすると、エラーになります。Excel 2024 build 19929 では 1004: アプリケーション定義またはオブジェクト定義のエラーです が表示されました。ここで誤解しやすいのが、点滅対象セルの「ロック」を外しただけでは足りないという点です。書式変更は「ロック」ではなく「ユーザーに許可する操作」側で制御されています。そのため、保護シート設定ダイアログで 「セルの書式設定」 にチェックを入れる必要があります。VBA からは Worksheet.Protect AllowFormattingCells:=True 相当の保護のかけ直しに対応します。実機でも、通常の Worksheet.Protect 直後は 1004 エラー、Protect(AllowFormattingCells:=True) 後は色変更が成功しました。マクロ側で Worksheet.Unprotect → 色変更 → Worksheet.Protect の順に明示的に解除・再保護する書き方もあります。

保護シートに対する色変更で発生する実行時エラー 1004 ダイアログ
保護シートでの色変更は実行時エラー 1004。保護解除か、許可設定の見直しが必要。

シート名を変えたら BlinkSheetName も変える

コード A / B では点滅対象のシート名を Public Const BlinkSheetName(または BlinkRangeSheet)に切り出しています。Excel 上でシート名を Sheet1 から 発注台帳 のように変更した場合は、この定数も同じ値に書き換えてください。書き換え忘れると Worksheets("Sheet1")9: インデックスが有効範囲にありません が発生します。シート名とセル番地を分けて定数化したのは、ここを書き換える箇所を 1 行に集約するためです。

「閉じ忘れ」の保険として Workbook_BeforeClose

万が一 StopBlinking を呼ばずにブックを閉じてしまっても、コールバックが暴走することはありません。マクロブックが閉じた時点で予約は事実上無効化されることを実機で確認しました。とはいえ、念のため次のコードを ThisWorkbook モジュールに入れておくと、閉じる動作と同時に停止マクロが走るため安心です。

Option Explicit

Private Sub Workbook_BeforeClose(Cancel As Boolean)
    On Error Resume Next
    StopBlinking
    StopBlinkingMulti
    On Error GoTo 0
End Sub

標準モジュールではなく ThisWorkbook モジュールに貼る点に注意してください。標準モジュール側に置くと、ブックを閉じるときのイベントとして発火しません。

条件付き書式では時間で点滅できない理由

「条件付き書式に =MOD(SECOND(NOW()),2)=0 のような式を書けば、1 秒ごとに色が切り替わるのでは」というアイデアは自然ですが、結論から言うと これでは点滅しません。Excel は時間経過だけでは NOW() の値を再評価しないからです。

MOD(SECOND(NOW()),2)=0 を使った条件付き書式ルールの設定ダイアログ
=MOD(SECOND(NOW()),2)=0 を条件付き書式に設定。設定自体は通るが、自動では点滅しない。

Excel 2024 build 19929 で実機検証した結果、=NOW() を入れたセルを 8 秒間そのまま放置しても、その間の NOW() の値は完全に止まったまま(たとえば 04:32:51.440000)でした。値が進むのは、明示的に Application.CalculateF9 キー押下、別セルへの入力など 「再計算してほしい」というトリガーが来たときだけです。ループ中にセルの DisplayFormat を読み取る行為そのものが条件付き書式の再評価を引き起こすこともあります。これで「sleep 中に色が変わった」と錯覚しがちですが、実際には観測行為の副作用です。

観点条件付き書式(静的)条件付き書式 + NOW()VBA Application.OnTime
設定の手間UI から数クリックUI + 数式VBA コードが必要
マクロ警告なしなしあり(.xlsm 必須)
値の変化で色を変える
時間ベースの自動点滅不可不可(NOW 値が更新されない)可能
操作で偶発的に色が変わるなしあり(F9・入力・再計算)なし
配布形式.xlsx で配布可.xlsx で配布可.xlsm 必須

視覚的に強い注意喚起(期限警告・ヒューマンエラー防止など)を求めるなら、条件付き書式単独ではなく、本記事の Application.OnTime 方式を採用するのが現実的です。逆に「値が一定範囲を超えたときだけ赤」のような 値依存の静的な強調は、引き続き条件付き書式が向いています。

応用|入力をトリガーに自動で点滅させる

「在庫数が 0 以下になったセルだけ自動で点滅させたい」のようなケースでは、Worksheet_Change イベントから StartBlinking を呼ぶ方法が使えます。シートモジュール(プロジェクトエクスプローラーで対象シート名をダブルクリックして開くモジュール)に次のコードを追加します。下の例はコード A(1 セル版)を併用する想定です。コード B(複数セル版)だけを使っている場合は StartBlinkingStartBlinkingMulti に置き換えてください。

Private Sub Worksheet_Change(ByVal Target As Range)
    Dim watched As Range
    Set watched = Me.Range("C2:C100")   ' 在庫数の列

    If Intersect(Target, watched) Is Nothing Then Exit Sub

    Dim cell As Range
    For Each cell In Intersect(Target, watched).Cells
        If IsNumeric(cell.Value) And cell.Value <= 0 Then
            StartBlinking
            Exit Sub
        End If
    Next cell
End Sub

注意点として、マクロ実行中に Application.EnableEvents = False でイベントを止めても、Application.OnTime によるコールバックは止まりません。予定どおり発火することを Excel 2024 build 19929 実機で確認しました。「EnableEvents = False にすれば点滅も止まる」と誤解されがちです。止めたい場合は StopBlinking を呼ぶか、g_BlinkActive = False を直接立ててください。

まとめ|どの手段を選ぶか

  • 1 秒間隔の「自動点滅」を実現できるのは Application.OnTime を使った VBA マクロだけです。
  • 停止できないトラブルの大半は、OnTime の引数完全一致罠か、停止フラグの欠如です。本記事のコード A / B は両方を入れています。
  • 別ブックを開いて作業する可能性があるなら、Application.OnTimeRange の両方をブック・シートで完全修飾してください。
  • 条件付き書式での時間点滅は仕組み上不可能ですが、値依存の静的な強調には引き続き向いています。
  • Ctrl + Z で戻せる履歴は VBA 経由の色変更では安定しないので、停止マクロから元の色を復元する運用にしてください。

マクロ実行のセキュリティ設定の手順も参考になります。マクロ有効ブックを開いたときの警告解除や信頼設定について、詳しく扱っています。

Next Read

このあと読む記事

今の内容に近い記事から、次の1本と補助記事を続けて見つけられるようにしています。

Keep Exploring

このテーマをさらに探す

同じテーマの入口記事と更新記事を、一覧の形でまとめています。

コメント