19:42:45)追加された行はこの色です。削除された行はこの色です。[#g0ca99f0]#column(注意){{この記事の最新版は「[[時間のかかる処理の進行状況を表示する>http://dobon.net/vb/dotnet/programing/displayprogress.html]]」で公開しています。この記事の最新版は「[[時間のかかる処理の進行状況を表示する>https://dobon.net/vb/dotnet/programing/displayprogress.html]]」で公開しています。}}大きなファイルを読み込んだり、大量のファイルをコピーするなどその処理に時間がかかるとき、ユーザーにアプリケーションがフリーズしたのではないかという心配をさせないように、処理の進行状況をメッセージやプログレスバーで表示する方法を説明します。ここでは単純な例として、次のようなアプリケーションを作成します。WindowsフォームにLabelコントロール(Label1)とProgressBarコントロール(ProgressBar1)とButtonコントロール(Button1)を貼り付け、Button1をクリックすると1秒おきにLabel1とProgressBar1の内容を変えて(1から10までカウントアップする)表示されるようにします。何も考えないで、Label.TextプロパティとProgressBar.Valueプロパティを変更し、それ以外何もしないでどのように表示されるか確かめてみましょう。}}}}上記のコードを実行すると、ProgressBarは1秒おきにバーが伸びていきますが、Labelに表示される文字列は変化することなく、Button1_Clickイベントハンドラを抜けてはじめて"10"と表示されます。これは、ProgressBarコントロールがそのValueプロパティが変更されるとコントロールを再描画するのに対して、LabelコントロールはTextプロパティを変更してもそうしないためです。ほとんどのコントロールはLabelコントロールと同様の挙動となるでしょう。ProgressBarコントロールのようにLabelコントロールでも1秒おきに内容を変更して表示するためには、Label.Textプロパティを設定後に、Updateメソッドを呼び出してコントロールを再描画するようにします。}}}}進行状況を表示するだけであれば、このようにUpdateメソッドを使うのが最も簡単で、安全な方法です。この方法では処理の間、フォームやコントロールを一切操作することが出来なくなります。このような状況が好ましくなければ、Application.DoEventsメソッドを使うか、マルチスレッド化するという方法をとる必要があります。Application.DoEventsメソッドを呼び出すと、キュー内で待機中のイベントを処理することができるため、次のようにループ内にDoEventsを入れることにより、フォームやコントロールが再描画されるのはもちろんのこと、フォームの移動やクリックなど、ユーザーによる操作も可能になります。}}}}Application.DoEventsメソッド(あるいはマルチスレッドでも同じことが言えます)を使う時に注意すべきことは、DoEventsメソッドが呼び出された時に発生しては困るすべてのイベントを把握し、それらが発生しないように対策を施す必要があるということです。上の例では、明らかにButton1_Clickイベントハンドラを処理中にもう一度Button1_Clickイベントハンドラが呼び出されてはいけません。よってこのようなことが起こらないように、ループの前にButton1のEnabledプロパティを無効にし、終了後に再び有効にするといった処理が必要になります。DoEventsメソッドならではの欠点もあります。イベントが処理されるのはDoEventsメソッドが呼び出された時だけですので、例えば上記の例でユーザーがフォームの「閉じる」ボタンをクリックしてもすぐには閉じなかったり、タイミングによっては閉じなかったりします。DoEventsメソッドが呼び出されイベントが処理される時、その処理がすべて終わるまでDoEventsメソッド以降の処理はブロックされます。上記の例の場合、フォームをマウスで移動している間ループ処理は中断され、移動をやめてからようやくカウントアップが再開されます。このようなDoEventsメソッドの欠点はマルチスレッドを使用することにより、解消されます。.NETのマルチスレッドプログラミングについては、このメールマガジンの第19号から第26号まで散々やってきましたので、ここでは一切説明せず、いきなりサンプルを示します。このサンプルでは、カウントアップするメソッドをメインとは別のスレッドで処理しています。ここでは、Button1がカウントアップ中にクリックされないように、Button1のEnabledプロパティをいじっています。en;}}}以上のように、Application.DoEventsメソッドを使う方法と、マルチスレッドによる方法は、Updateメソッドのみを使った方法と比べて、考慮すべき事柄が多く、危険性が高く、面倒であることがわかります。よってこれらの方法は初心者向きとは言えませんが、様々な局面(例えば、次に紹介するTips)においては有効な方法であることは間違いありません。[#pe7c7d98]#column(注意){{この記事の最新版は「[[時間のかかる処理をユーザーが停止できるようにする>http://dobon.net/vb/dotnet/programing/abortprocess.html]]」で公開しています。この記事の最新版は「[[時間のかかる処理をユーザーが停止できるようにする>https://dobon.net/vb/dotnet/programing/abortprocess.html]]」で公開しています。}}次に、時間のかかる処理をユーザーがキャンセルボタンをクリックするなどして、途中で停止できるようにする方法を考えます。先の「時間のかかる処理の進行状況を表示する」で考察した通り、Application.DoEventsメソッドを使うか、マルチスレッドを使うかということになるでしょう。DoEventsメソッドを使用する場合は、キャンセルボタンがクリックされたらフラッグを立てるようにして、ループ内ではDoEventsメソッドを呼び出した後でフラッグをチェックし、フラッグが立っていればループを抜けるようにすればよいでしょう。このようにして「時間のかかる処理の進行状況を表示する」のサンプルを改良してコードを以下に示します。キャンセルボタン(Button2)をフォームに追加し、Button2をクリックすると、カウントアップ処理が中止されるようにしています。"ユーザーにより中止されました。"ユーザーにより中止されました。true;}}}次はマルチスレッドを使った方法です。この場合、キャンセルボタンが押された時に、処理中のスレッドのThread.Abortメソッドを呼び出してスレッドを中止させればよいという考えもあるでしょう。Abortメソッドを使って別のスレッドを終了させることは、どこで中断されるか予測できなく、それだけ危険といえます。よって、Abortメソッドはなるべく使わないようにして、フラッグを使って適当なタイミングでループを終了させる方法をお勧めします。以下にこのような方法により、「時間のかかる処理の進行状況を表示する」で紹介したコードにキャンセルボタン(Button2)の処理を付けたコードを示します。"ユーザーにより中止されました。"ユーザーにより中止されました。en;}}}最後にダイアログ使って進行状況を表示し、キャンセルできるようにする例を紹介しようかと考えていたのですが、このコードがちょっと長いため、ここに載せることができなくなってしまいました。このコードはその内に私のサイトで紹介します。