【エクセルVBA】マクロ実行時の画面のチラつきをなくして高速化する(Application – ScreenUpdating)

マクロを実行すると画面がチラついてしまうのだけど、これどうにかならないかなぁ。
それと、大した処理していないのに実行速度が思ったより遅いけど、何故だろう?

もしかして、たくさんのブックを開いたりしている?

あ、その通りです!
フォルダ内のすべてのブックを開いて特定のセルの値を別ブックに一覧にする、という処理なんですけど、画面上でエクセルがガチャガチャするというか。
マクロ実行しているのでそういうものなんですかね。。

マクロ実行時に画面更新を無効にすればチラつきは解消できそうね。
処理速度も改善できるかな。

ということで、画面のチラつき防止と、それに伴う処理速度の高速化について紹介します。

はじめに

テストコードとして、

フォルダ内のすべてのエクセルをひとつずつ開いてA1セルの値を取得し一覧化する

という処理にしてみます。

例えば、エクセルで作ったたくさんのアンケート結果があったとして、そこからある設問の回答のみ取得して一覧化したい、というようなイメージです。

要するに、たくさんのエクセルを開く必要がある処理です。

この処理で、画面更新を無効にした場合とそうでない場合の画面のチラつき具合と処理速度の違いを比較してみます。

テストコード

Sub Test()

    Const FOLDER_PATH As String = "C:\00_myenv\10_macro\01_test\many_files_folder"

    '画面更新を無効(False)にする
    Application.ScreenUpdating = False
    
    '処理時間を計測開始
    Dim startTime As Double: startTime = Timer
    
    '指定フォルダ内のすべてのエクセルをひとつずつ開いてA1セルの値を取得する
    Dim i As Long: i = 1
    Dim file As Variant
    For Each file In CreateObject("Scripting.FileSystemObject").GetFolder(FOLDER_PATH).Files
        Dim wb As Workbook
        Set wb = Workbooks.Open(file.Path, ReadOnly:=True)
        ThisWorkbook.Worksheets("取得結果").Cells(i, 1) = wb.Worksheets("Sheet1").Range("A1")
        i = i + 1
        wb.Close
    Next file
    
    '処理時間を表示する
    MsgBox Timer - startTime & "秒"

    '画面更新を有効(True)にする(処理の終わりに元に戻しておく)
    Application.ScreenUpdating = True
    
End Sub

テストデータ

以下のような簡単なエクセルを準備して、フォルダに複数個入れておきます。

<Sheet1のA1セルに数値を設定したエクセル>

テストデータ

<エクセルを配置したフォルダ>

テストデータの配置フォルダ

実行結果

テストコード6行目の「Application.ScreenUpdating = False」の1行を

  • コメントアウトして、画面更新を無効にしない場合
  • コメントアウトせず、画面更新を無効にした場合

で、画面のチラつきと処理時間の違いを比較します。

まずは、画面のチラつきについて、以下の動画のような違いがあります。

ファイル10個で実行しました。

画面更新を
無効にしない場合

画面更新を
無効にした場合

画面更新を無効にしない方はエクセルを開くたびに画面描画され、画面がチラついてしまいます。

次に、処理時間の違いについてです。

対象とするファイルは10個、50個、100個で、それぞれ3回ずつ実行したおおよその結果です。

ファイル画面更新を
無効にしない場合
画面更新を
無効にした場合
10個約5秒約3秒
50個約25秒約15秒
100個約50秒約30秒

実行環境により処理時間の増減があると思いますが、画面更新を無効にした場合の方が速いです。

※ファイル10個の処理時間が上に置いた動画の方が数秒長くなっていますが、これは画面録画した際の録画ソフトの負荷があったためです。

説明

ということで、画面のチラつき防止、処理速度の高速化をしたい場合は、

Sub Test()
    '画面更新を無効(False)にする
    Application.ScreenUpdating = False

    '---------------------------------------
    '画面がチラつく処理をここに書く
    '---------------------------------------

    '画面更新を有効(True)にする(処理の終わりに元に戻しておく)
    Application.ScreenUpdating = True
End Sub

という実装の仕方をします。

また、最後に「True」に戻しているところですが、この1行を明示的に書かなくてもマクロ終了時に自動で「True」に戻るのですが、個人的に明示的に元に戻しておかないと何となく気持ち悪いので書いてます。

注意点

ここで、ご注意いただきたいことが3点ほどあります。

1点目は、

「画面更新を無効にすれば、どんな処理でも速度向上につながる」ということではありません!

「マクロ実行中に画面更新が頻繁にある処理の場合に効果がある」

ということです。

ですので、画面更新があまりない処理では、ほとんど意味はありません。

2点目は「ループ処理の中に「Application.ScreenUpdating」を書かない」です。

ループの回数だけ無効/有効の設定処理が実施されてしまうので、処理速度が遅くなってしまいます。

3点目は「Application.ScreenUpdating」を書く前に、コードに「Select」が多用されていないか確認してみてください。多用されている場合は、実装方法自体を見直すことをお勧めします。

マクロを始めて間もない方のソースコードでよく見かけるのが、シートやセルを「Select」で選択しているものです。

エクセルの「マクロの記録」でマクロ作成すると「Select」が多用されているので、これを参考にしているのかと思いますが、「Select」は使わないことを推奨します。

例として、シートやセルを頻繁に「Select」している場合を見てみます。

'------------------------------------------------------------------
'Sheet1とSheet2を交互に切り替えて1列目に数値を設定、を1000回繰り返す
'------------------------------------------------------------------
Sub Test2()
    Dim i As Long
    For i = 1 To 1000
        ThisWorkbook.Worksheets("Sheet1").Select    'Sheet1をSelect
        Cells(i, 1).Select                          'Sheet1のセルをSelect
        Selection = i
        
        ThisWorkbook.Worksheets("Sheet2").Select    'Sheet2をSelect
        Cells(i, 1).Select                          'Sheet2のセルをSelect
        Selection = i
    Next i
End Sub

このコードをそのまま実行すると、画面はちらつきますし、処理時間は「約10秒」となります。

そこで「Application.ScreenUpdating = False」を追記して実行すると、画面のチラつきがなくなり、処理時間も「約1.5秒」となります。

ところが「これで解決!」ではありません。

というのも、上記のソースコードを「Select」を使わず以下のように実装すれば同じ処理結果となり、画面もチラつかず、処理速度も遅くなりません。

'------------------------------------------------------------------
'上記Test2のコードを「Select」を使用せず、同じ処理結果となるように実装したもの
'------------------------------------------------------------------
Sub Test3()
    Dim i As Long
    For i = 1 To 1000
        ThisWorkbook.Worksheets("Sheet1").Cells(i, 1) = i
        ThisWorkbook.Worksheets("Sheet2").Cells(i, 1) = i
    Next i
End Sub

処理時間は「約0.1秒」でした。

これは非常に小さなコードですが、大きめのコードで「Select」が多用されていることを考えれば、こちらの方がコードの可読性もよくなり、保守性もあがることは一目瞭然です。

まとめ

「Application.ScreenUpdating」による画面更新の無効/有効の設定ですが、「画面がチラつくような処理をする場合は非常に有効である」ということでした。

また、「Select」の多用により画面がチラついている場合は、実装方法の見直しを検討することをお勧めします。

ちなみに、今回紹介したテストコードは「ブックをたくさん開く必要がある処理」というものでした。

「Select」を多用したコードで紹介してもよかったのですが、「Select」を使用すること自体がよくないため、「ブックをたくさん開く必要がある処理」を扱いました。

ブックを開くと画面描画が入るため、画面更新を無効にすることの有効性を確認するのに効果的と思ったためです。

最後に、画面更新があろうがなかろうが「Application.ScreenUpdating」の設定を入れておいても、それ自体は特に悪さはしないので、常に書くようにしていても問題はないと思います。

以上、ご覧いただきありがとうございました。