アプリで待ちが発生したとき、スマホアプリみたいに
「クルクルする待っててね!ってヤツほしいな……」
って思いませんか?
C#のWindows Formsアプリで、そのクルクル……
「ローディングアニメーション」を作る方法についてです!
- 作ったサンプルアプリについて
- ローディングアニメーションの追加
- BackgroundWorkerの設定
- 各処理の作成
- ローディングアニメーションの表示/非表示切替
- Formのロード処理
- クリックイベント
- 働け!~DoWork~
- 進捗は!?~ProgressChanged~
- 参考
- あとがき
作ったサンプルアプリについて
サンプルプロジェクトは、GitHubに置いてあります。
今回はこんな感じのUIを作りました。
ついでで色々付けましたが、大事なのは、ボタンを画像です。
ローディングアニメーションの追加
ローディングアニメーション自体は、GIFアニメ画像をPictureBoxに設定しているだけです。
セットしたPictureBoxを選択した状態で、プロパティを開きます。
- 表示
- Image : 設定したい画像を選択
で、GIFアニメ画像を設定することができます。
このPictureBoxの表示/非表示を切り替えることで、ローディングアニメーションを実装していきます。
使用したGIF動画アニメ画像
GIFアニメ画像自体は、こちらのものを使用させて頂きました。
恐らくWebとかで使うようなものだと思うので、アプリで使おうとすると小さいですが、テストなので。
BackgroundWorkerの設定
ツールボックスから「BackgroundWorker」をセットします。
「back」とかで調べると、一発で出てきます。
追加するとデザイナの下部の枠に追加されます。
プロパティの設定
セットしたBackgroundWorkerを選択した状態で、プロパティを開きます。
- 非同期
- WorkerReportsProgress : True
に設定します。
これによって、処理進捗更新時に実行される関数が使えるようになります。
イベントの設定
セットしたBackgroundWorkerを選択した状態で、プロパティを開きます。
イベント(雷マーク)を開いたら、
- DoWork
- ProgressChanged
- RunWorkerComplete
の3つ全てのイベントを作成します。
それぞれダブルクリックすれば、「オブジェクト名+関数名」で自動生成されます。
各処理の作成
それでは処理を作成します。
作成する処理の流れとしては、
- ボタンを押すと処理が開始される(今回はダミー処理)
- 処理が進行していく(この間、アニメが再生される)
- 処理完了でポップアップ表示+アニメ停止
といった感じです。
ローディングアニメーションの表示/非表示切替
ローディングアニメーション自体の表示/非表示を切り替えるための関数です。
private void VisibleProgressImage(bool isVisible) => pictureProgress.Visible = isVisible;
C#って1行で終わるような関数をこんなふうに書けるんですね……わかりやすい……
解説すると、引数出渡されたbool値を、ローディングアニメーションのVisibleにセットしているだけです。
Formのロード処理
フォームのロード時に、ローディングアニメーションを非表示の状態にしています。
private void Form1_Load(object sender, EventArgs e) { this.StopTextLabel(); this.VisibleProgressImage(false); // ← }
クリックイベント
ボタンのクリックイベントはこんな感じ。
private void button1_Click(object sender, EventArgs e) { this.button1.Enabled = false; this.StopTextLabel(); this.VisibleProgressImage(true); // ← this.progressBar1.Value = 0; this.backgroundWorker1.RunWorkerAsync(); // ← }
ローディングアニメーションを表示させます。
「backgroundWorker1.RunWorkerAsync()」を実行することで、BackgroundWorkerに
「非同期で動き初めて!」
と開始指示をします。
これによって、BackgroundWorkerで設定していた、各イベントが動作するようになります!
働け!~DoWork~
DoWorkは、メインの処理をしてくれるところです。
// 別スレッドでの動作 private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { for ( int i = 1; i <= 100; i++ ) { this.cnt = i; this.Processing(); // Thread.Sleep(100); int progress = this.cnt; string msg = "実行中... "; this.backgroundWorker1.ReportProgress(progress, msg); } }
今回はダミーの処理として、100msスリープを100回行っています。
ループの最後に実行している、「backgroundWorker1.ReportProgress()」。
これで
「今の進捗はこうだよ!」
というのを、通知しています。
通知されると、「ProgressChanged」が、その通知を受け取ってくれます。
ReportProgressの引数は
引数 | 型 | 内容 |
---|---|---|
percentProgress | Int32 | 進捗値(0~100[%]) |
userState | Object | 状態オブジェクト |
userStateは、Object型なので、任意のオブジェクトを渡すことができます。
今回はstringでメッセージを送っています。
DoWorkではフォームオブジェクトの操作はNG
DoWork内で、ラベルやプログレスバーの更新等、フォームオブジェクトを操作するのはNGです。
例えば、DoWorkの中でプログレスバーを操作してみるとします。
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { for ( int i = 1; i <= 100; i++ ) { ~略~ this.progressBar1.Value = i; ~略~ } }
すると実行時に、このような例外が発生します。
System.InvalidOperationException: '有効ではないスレッド間の操作: コントロールが作成されたスレッド以外のスレッドからコントロール 'progressBar1' がアクセスされました。'
なので、この辺の操作のためにも「ProgressChanged」が必要となってきます。
進捗は!?~ProgressChanged~
backgroundWorker1.ReportProgress()で通知されたものを受け取ります。
// スレッドの進捗を受け取る private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) { this.label1.Text = (string)e.UserState + " " + e.ProgressPercentage.ToString() + "%"; this.progressBar1.Value = e.ProgressPercentage; }
引数の「ProgressChangedEventArgs e」の中に、通知の内容(引数に設定した値)が入っています。
引数 | 型 | 内容 |
---|---|---|
e.ProgressPercentage | Int32 | 進捗値(0~100[%]) |
e.UserState | Object | 状態オブジェクト |
この値を使って、メッセージ表示を行ったり、プログレスバーを進めたりすることができます。
インストーラのインストール中の表示って、こういう感じで作ってそうですね。
仕事終わったわ~RunWorkerCompleted~
DoWork内の処理が全て完了すると、RunWorkerCompletedが呼び出されます。
これで終了処理を行えば、ミッションコンプリートです!
// スレッド完了通知を受け取る private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { MessageBox.Show("処理完了!"); // ← this.VisibleProgressImage(false); // ← this.StopTextLabel(); this.button1.Enabled = true; }
今回はメッセージボックを表示して、ローディングアニメーションを非表示にしています。
その他はUI周りのものです。
参考
コチラを参考にしました。ありがとうございました!
あとがき
C#のWindows Formsアプリで、処理中にクルクルするローディングアニメーションを作る方法についてでした!
スレッド的な頭が必要かもしれないので、はじめは微妙にとっつきにくいかも知れません。
別スレッド的に処理を作らないといけないので、既存処理に組み込む場合は、多少なりともインパクトがあると思うのでご注意くださいませ。
気になった方は是非試してみてください!