処理状況表示 ①今何やってるの?どこまで進んだ?

YouTube VBA

プログラム実行時にどこまで進んだか、あとどれくらい時間がかかるのか表示させましょう!
ユーザーに使ってもらうことも視野に入れましょう!

ユーザーフォームで処理状況を表示しながら、バックグラウンドでプログラムを実行します

ユーザーフォーム作成

まず、ユーザーフォームを作成しましょう!

VBEから
挿入→ユーザーフォーム
オブジェクト名UserForm1が作成される

ツールボックスも表示しておきましょう

表示されない場合は
表示→ツールボックスを選択する

プロパティウインドウのフォームの項目変更

オブジェクト名:UserForm1→frmTestに変更
Caption   :UserForm1→実行中…

コマンドボタン追加

ツールボックスからコマンドボタンを選択して、フォームにドラッグする

オブジェクト名btnClose
Caption閉じる

同様にテキストボックスを追加する

オブジェクト名:txtJyoukyou

ラベルを追加

オブジェクト名:変更なし(プログラムに使わないので)
Caption:回目/10000回中
Fontサイズ18

その他変更

画面を見ながら、位置や大きさを変更する

Font   :フォントを変更したり、フォントサイズを変更します
TextAlign:文字の位置 センタリングを選びます

フォームの表示/非表示

フォームの表示
作成したフォームを表示してみましょう!
標準モジュールに以下を追加するだけです

ユーザーフォーム名.Show

frmTest:作成したフォーム名
Show:ユーザーフォームを表示

標準モジュール

Sub Test()
  frmTest.Show
End Sub

フォームの非表示(フォームのアンロード)

    Unload ユーザーフォーム名

フォームの閉じるボタンをダブルクリック

フォームのプログラム

Private Sub btnClose_Click()

End Sub

と表示されるので、
btnCloseは作成したコマンドボタンのオブジェクト名)

Private btnClose_Click()
  Unload frmTest
End Sub

標準モジュールのTestを実行し、フォームを表示します
表示されたフォームの閉じるボタンをクリックし、フォームを閉じます
フォームを開き・閉じることができましたか?

フォームのプログラム作成

フォームのコードの表示方法

frmTestをクリックして、表示からコードを選択(F7キーでも)

または、frmTest右クリックして、コードの表示選択

先程作成したフォームのプログラムが表示されましたか?

フォームのプログラム

フォームが表示された時の処理を追加していきます

コードウインドウの左上
オブジェクトボックスからUserFormを選択し、

コードウインドウの右上
プロシージャボックスからActivateを選択

UserForm_Activateが作成されましたね!
これがユーザーフォームが表示された時、実行されるプログラムです


このUserForm_Activateからmainルーチンを呼び出します
このmainに時間のかかる処理を書く
(ここに直接プログラムを書いてもOKですが、
時間のかかる処理ですから通常ものすごい長いプログラムのはずです)

Private Sub btnClose_Click()
    Unload frmTest
 End Sub
Private Sub UserForm_Activate()
    Call main
 End Sub

標準モジュールプログラムの作成

標準モジュールのプログラム

標準モジュールにmainルーチンを書きます
ここに状況を表示させるプログラムを追加します

今回は10000回の処理中どこまで進んだかを表示!

frmTest.tと書くと候補が表示されます
作成したテキストボックスtxtJyoukyouを選び、

続けて.vと書き、候補からValueを選ぶ

frmTest.txtJyoukyou.Value = iとして、
txtJyoukyou処理の状況(iの値)を表示します

描画を更新するためには、DoEventsが必要です
DoEventsがなければ、状況表示ができません(あっという間に終わってしまう)
コメントアウトして実行してみてください!(DoEventsの意味がわかるでしょう~)

Sub main()
    Dim i As Long
    For i = 1 To 10000
        DoEvents     'OSに処理を返す(画面描画を更新)
        frmTest.txtJyoukyou.Value = i
    Next i
End Sub

F5キーを押して、Testを実行してみましょう
フォームがアクティブになった時に、mainルーチンが呼び出され、
作成したフォームのテキストボックス処理状況が表示されます

Sub Test()
   frmTest.Show
End Sub

実行ボタン作成

Excelからマクロを実行できるように
実行ボタンを作成し、
frmTestを表示するTestと紐づけます
ボタンを押して実行してみましょう!

閉じるボタンの変更

処理の途中で、閉じるボタンを押すと途中で終了し、フォームも消えてしまいます

続きを実行したいことありますよね
やめるのやめたい~
閉じるボタンをもう少し修正しましょう!

閉じるボタンを押された時の処理

btnClose_ClickにMsgBoxで”ほんとに終了していいの?”ときいてみましょう!

MsgBox(表示メッセージ[,ボタン][,タイトル])

ボタンにvbYesNo(4)を指定します
はい】【いいえ】ボタンを表示します

MsgBox(“処理を中止しますか?”, vbYesNo)

MsgBoxの戻り値を取得することにより、閉じる判断ができる

MsgBoxの戻り値

戻り値が【はいvbYes(6)かどうか判断し
vbYesでなければ、フォームを閉じずExit

Private Sub btnClose_Click()
    Dim CloseYesNo As Long
    CloseYesNo = MsgBox("処理を中止しますか?", vbYesNo)
    If CloseYesNo <> vbYes Then Exit Sub
    Unload frmTest
    End
End Sub

フォームが消去されても、EXCELは実行され続けますので、
プログラムを終了させるため、Unload フォームの後にEndを入れましょう


はい:プログラム終了
いいえ:プログラム続行

終了処理

処理が終わっても、MsgBoxが表示される
これいらないよね!

処理が終了したら、
MsgBoxを表示して、フォームを消去します
UserForm_Activatemainどちらかに加えてください…どちらがいいかな?

Private Sub UserForm_Activate()
    Call main

    '処理終了のメッセージを表示
    MsgBox "処理が終了しました。"
    'フォームをアンロードする
    Unload frmTest
 End Sub
Sub main()
    Dim i As Long
    For i = 1 To 10000
        'OSに処理を返す(画面描画を更新)
        DoEvents
        frmTest.txtJyoukyou.Value = i
    Next i

    '処理終了のメッセージを表示
    MsgBox "処理が終了しました。"
    'フォームをアンロードする
    Unload frmTest
End Sub

mainには終了の処理は関係ないので、
UserForm_Activateに入れる方がいいかなと思います
お好みで…

Module1

Sub Test()
    frmTest.Show
End Sub

Sub main()
    Dim i As Long
    For i = 1 To 10000
        DoEvents    'OSに処理を返す(画面描画を更新)
        frmTest.txtJyoukyou.Value = i
    Next i

End Sub

frmTest

Option Explicit
Private Sub btnClose_Click()
    Dim CloseOkNo As Long
    
    CloseOkNo = MsgBox("処理を中止しますか?", vbYesNo)
    If CloseOkNo <> vbYes Then Exit Sub
    
    'フォームをアンロードする
    Unload frmTest
    End
End Sub

Private Sub UserForm_Activate()

    Call main
    
    '処理終了のメッセージを表示
    MsgBox "処理が終了しました。"
    'フォームをアンロードする
    Unload frmTest

End Sub

モーダルとモーダレス

今までは、モーダルでプログラムを組んできました
フォームを表示している間(Show~Unloadまで)、EXCELを操作できません

状況表示している間に画面をクリックしてみてください
”ポワンポワン”とすべての操作ができません

modalとは…

モードがある状態。つまり、システムが特定の機能の使用に制限された状態。
ユーザーが自由に操作を行えなくなることと、モード別に機能の意味や振る舞いが変化することから、ユーザーインターフェースのデザインでは、できる限りモードを設けないほうがよいとされる。

ttps://www.sociomedia.co.jp/1320

modelessとは…

モードがない状態。ユーザーインターフェースをデザインする際に目指すべき状態。
状況に依存した機能制限がなく、自由な手順でタスクを進行することができ、
かつ特定の操作がシステムによって常に一定に解釈される状態。

https://www.sociomedia.co.jp/326

ユーザーインターフェースの観点からモードレスの方が推奨されているのですね~

frmTest.Show vbModal   モーダル(規定値)
frmTest.Show  ←今まではこれ!        
vbModalは、既定値ですので、省略するとモーダルとなります

ここまでのモーダルプログラム

Module1

Option Explicit
Sub Test()
    'frmTest.Showでも可
    frmTest.Show vbModal   'モーダル(規定値)
End Sub

Sub main()
    Dim i As Long
    For i = 1 To 10000
        DoEvents    'OSに処理を返す(画面描画を更新)
        frmTest.txtJyoukyou.Value = i
    Next i

End Sub

frmTest

Option Explicit
Private Sub btnClose_Click()
    Dim CloseOkNo As Long
    
    CloseOkNo = MsgBox("処理を中止しますか?", vbYesNo)
    If CloseOkNo <> vbYes Then Exit Sub
    
    'フォームをアンロードする
    Unload frmTest
    End
End Sub

Private Sub UserForm_Activate()

    Call main
    
    '処理終了のメッセージを表示
    MsgBox "処理が終了しました。"
    'フォームをアンロードする
    Unload frmTest

End Sub

frmTest.Show vbModeless  モードレス
フォームを実行中でも、EXCELの操作ができます


フォームを実行中でも、EXCELの操作ができるので、
フォーム名.Show vbModelessの後にプログラムを置いて実行させることができます

プログラムは以下の様にSimple!!
フォームのプログラムbtnClose_Clickのみ
UserForm_Activateは要らなくなりました

モードレスプログラム

Module1

Option Explicit

Sub Test()
    'frmTest.Show vbModal   'モーダル(規定値)
    frmTest.Show vbModeless 'モードレス
    
    Call main         'フォームを表示した後にExcelのプログラムが書ける

    '処理終了のメッセージを表示
    MsgBox "処理が終了しました。"
    'フォームをアンロードする
    Unload frmTest
End Sub

Sub main()
    Dim i As Long
    For i = 1 To 10000
        DoEvents    'OSに処理を返す(画面描画を更新)
        frmTest.txtJyoukyou.Value = i
    Next i

End Sub

フォーム

Option Explicit
Private Sub btnClose_Click()
    Dim CloseOkNo As Long
    
    CloseOkNo = MsgBox("処理を中止しますか?", vbYesNo)
    If CloseOkNo <> vbYes Then Exit Sub
    
    'フォームをアンロードする
    Unload frmTest
    End
End Sub

モーダル

フォーム名.Show

UserForm_Activateに
メイン処理を呼び出す Or
メイン処理を書く
(メイン処理の中で状況表示)

Unload フォーム名
終了ボタンを押された時 Or
メイン処理が終了したとき

モーダレス

フォーム名.Show vbModeless
メイン処理を呼び出す Or
メイン処理を書く
(メイン処理の中で状況表示)

Unload フォーム名
終了ボタンを押された時 Or
メイン処理が終了したとき

私見ですが、別ファイルを開いたり、他のシートをアクティブにできるので、
プログラムに注意が必要なのかも…?
ActiveWorkBookとかActiveSheetとかいう指定は避けた方がいいのかも…と思う
ThisWorkbookは大丈夫なのかな?

フォーム名.Repaint
Repaint メソッドは、指定されたフォームに対して、
画面の更新操作がある場合はそれを実行します
また、Repaint メソッドは、更新に伴って、フォーム上のコントロールの再計算も行います
どちらかでいいと思うのですが、両方書いてもいいかな?色々と試してください
Repaint入れると、ちらつく気がするのですが…

For i = 1 To 10000
        'OSに処理を返す(画面描画を更新)
        DoEvents
        frmTest.txtJyoukyou.Value = i
        '画面の更新操作
        frmTest.Repaint
    Next i

DoEvents:動作の安定性
Repaint :処理の速さ

動作の安定性ではDoEvents関数が勝り、処理の速さではRepaintメソッドが勝ります。https://excel.syogyoumujou.com/form/1_27.html

仮想処理状況を表示

仮想処理状況を表示するPG作成しました
実行してみてください

フォーム
オブジェクト名:frmTest
Caption:実行中…

コマンドボタン
オブジェクト名:btnClose
Caption:閉じる

テキスト
オブジェクト名:txtJyoukyou

ラベル
オブジェクト名:lblJyoukyou
Caption:なし
オブジェクト名:変更なし
Caption:回目/10000回中

モーダルVer.
 
Module1

Option Explicit
Sub Test()
    frmTest.Show
End Sub

Sub main()
    Dim i As Long
    
    'ここにFile 読み込み処理を書く
    Cells(8, 5) = "File 読み込み中"
    frmTest.lblJyoukyou.Caption = "File 読み込み中"
    For i = 1 To 3000
        'OSに処理を返す(画面描画を更新)
        DoEvents
        Cells(10, 5) = i
        frmTest.txtJyoukyou.Value = i
        'frmTest.Repaint        '画面がちらつくのでコメントに
    Next i
    
    'ここに計算処理実行処理を書く
    Cells(8, 5) = "計算処理実行中"
    frmTest.lblJyoukyou.Caption = "計算処理実行中"
    For i = 3001 To 7000
        'OSに処理を返す(画面描画を更新)
        DoEvents
        Cells(10, 5) = i
        frmTest.txtJyoukyou.Value = i
        'frmTest.Repaint        '画面がちらつくのでコメントに
    Next i
    
    'ここにFile 書き込み処理を書く
    Cells(8, 5) = "File 書き込み中"
    frmTest.lblJyoukyou.Caption = "File 書き込み中"
    For i = 7001 To 10000
        'OSに処理を返す(画面描画を更新)
        DoEvents
        Cells(10, 5) = i
        frmTest.txtJyoukyou.Value = i
        'frmTest.Repaint        '画面がちらつくのでコメントに
    Next i
    
    frmTest.lblJyoukyou.Caption = "処理終了しました"
    Cells(8, 5) = "処理終了しました"
End Sub

フォーム

Option Explicit
Private Sub UserForm_Activate()
    Call main
    '処理終了のメッセージを表示
    MsgBox "処理が終了しました。"
    'フォームをアンロードする
    Unload frmTest
 End Sub

 Private Sub btnClose_Click()
    Dim CloseYesNo As Long
    CloseYesNo = MsgBox("処理を中止しますか?", vbYesNo)
    If CloseYesNo <> vbYes Then Exit Sub
    Unload frmTest
    End
End Sub

モーダレスVer.
Module1

Option Explicit
Sub Test()
    frmTest.Show vbModeless  'モードレス
    Call main         'ここにメイン処理を書くことができる
  '処理終了のメッセージを表示
    MsgBox "処理が終了しました。"
    'フォームをアンロードする
    Unload frmTest
End Sub

Sub main()
    Dim i As Long
    
    'ここにFile 読み込み処理を書く
    Cells(8, 5) = "File 読み込み中"
    frmTest.lblJyoukyou.Caption = "File 読み込み中"
    For i = 1 To 3000
        'OSに処理を返す(画面描画を更新)
        DoEvents
        Cells(10, 5) = i
        frmTest.txtJyoukyou.Value = i
        'frmTest.Repaint        '画面がちらつくのでコメントに
    Next i
    
    'ここに計算処理実行処理を書く
    Cells(8, 5) = "計算処理実行中"
    frmTest.lblJyoukyou.Caption = "計算処理実行中"
    For i = 3001 To 7000
        'OSに処理を返す(画面描画を更新)
        DoEvents
        Cells(10, 5) = i
        frmTest.txtJyoukyou.Value = i
        'frmTest.Repaint        '画面がちらつくのでコメントに
    Next i
    
    'ここにFile 書き込み処理を書く
    Cells(8, 5) = "File 書き込み中"
    frmTest.lblJyoukyou.Caption = "File 書き込み中"
    For i = 7001 To 10000
        'OSに処理を返す(画面描画を更新)
        DoEvents
        Cells(10, 5) = i
        frmTest.txtJyoukyou.Value = i
        'frmTest.Repaint        '画面がちらつくのでコメントに
    Next i
    
    frmTest.lblJyoukyou.Caption = "処理終了しました"
    Cells(8, 5) = "処理終了しました"
End Sub

フォーム

Option Explicit
 Private Sub btnClose_Click()
    Dim CloseYesNo As Long
    CloseYesNo = MsgBox("処理を中止しますか?", vbYesNo)
    If CloseYesNo <> vbYes Then Exit Sub
    Unload frmTest
    End
End Sub

プログラムコード

YouTube動画のソースコードです

YouTubeでは、処理があちこち飛ぶのでCall mainとせず
UserForm_Activateにメイン処理を直接書いています

モーダルVer.
 
Module1

Sub Test()
    frmTest.Show vbModal   'モーダル(規定値)

End Sub
Sub main()
    'ここにメイン処理を書いてもOK!

End Sub

フォーム

Private Sub btnClose_Click()
    Dim CloseOkNo As Long
    
    CloseOkNo = MsgBox("処理を中止しますか?", vbYesNo)
    If CloseOkNo <> vbYes Then Exit Sub
    
    'フォームをアンロードする
    Unload frmTest
    End
End Sub

Private Sub UserForm_Activate()

    'メインの処理(orメインの処理を呼び出しても)
    Dim i As Long
    
    For i = 0 To 10000
        
        DoEvents  'OSに処理を返す(画面描画を更新)
        frmTest.TtxtJyoukyou.Text = i
        Cells(8, 3) = i
    Next i
    
    '処理終了のメッセージを表示
    MsgBox "処理が終了しました。"
    'フォームをアンロードする
    Unload frmTest

End Sub

モーダレスVer.
Module1

Sub Test()

    frmTest.Show vbModeless 'モーダレス
    
    Dim i As Long
    
    'メインの処理(orメインの処理を呼び出しても)
    For i = 1 To 10000
        DoEvents
        frmTest.TtxtJyoukyou.Text = i
    Next i
    
    'frmTest.lblOwari.Caption = "処理が終了しました"
    MsgBox "処理が終了しました"
    Unload frmTest
End Sub
Sub main()
    'ここにメイン処理を書いてもOK!
End Sub

フォーム

Private Sub btnClose_Click()
    Dim CloseYesNo  As Long
    
    CloseYesNo = MsgBox("処理を中止しますか?", vbYesNo)
    If CloseYesNo <> vbYes Then Exit Sub
    
    Unload frmTest
    End
End Sub


続編のこちらもどうぞ!
プログレスバーで処理状況を表示します

コメント