lisz-works

プログラミングと興味を貴方に

C# Windows Formsで処理中にクルクルするローディングアニメーションを作る方法

【スポンサーリンク】

C#

アプリで待ちが発生したとき、スマホアプリみたいに

「クルクルする待っててね!ってヤツほしいな……」

って思いませんか?

C#のWindows Formsアプリで、そのクルクル……

「ローディングアニメーション」を作る方法についてです!

作ったサンプルアプリについて

サンプルプロジェクトは、GitHubに置いてあります。

github.com

今回はこんな感じのUIを作りました。

サンプルアプリの動作

ついでで色々付けましたが、大事なのは、ボタンを画像です。

ローディングアニメーションの追加

ローディングアニメーション自体は、GIFアニメ画像をPictureBoxに設定しているだけです。

セットしたPictureBoxを選択した状態で、プロパティを開きます。

  • 表示
    • Image : 設定したい画像を選択

で、GIFアニメ画像を設定することができます。

PictureBoxのImageを設定

このPictureBoxの表示/非表示を切り替えることで、ローディングアニメーションを実装していきます。

使用したGIF動画アニメ画像

GIFアニメ画像自体は、こちらのものを使用させて頂きました。

www.pixelimage.jp

恐らくWebとかで使うようなものだと思うので、アプリで使おうとすると小さいですが、テストなので。

BackgroundWorkerの設定

ツールボックスから「BackgroundWorker」をセットします。

「back」とかで調べると、一発で出てきます。

BackGroundworkerをセット

追加するとデザイナの下部の枠に追加されます。

BackGroundworkerは下部の枠に追加される

プロパティの設定

セットしたBackgroundWorkerを選択した状態で、プロパティを開きます。

  • 非同期
    • WorkerReportsProgress : True

に設定します。

WorerReportsProgressをTrue

これによって、処理進捗更新時に実行される関数が使えるようになります。

イベントの設定

セットしたBackgroundWorkerを選択した状態で、プロパティを開きます。

イベント(雷マーク)を開いたら、

  1. DoWork
  2. ProgressChanged
  3. RunWorkerComplete

の3つ全てのイベントを作成します。

BackgroundWorkerのイベントを全て作成

それぞれダブルクリックすれば、「オブジェクト名+関数名」で自動生成されます。

各処理の作成

それでは処理を作成します。

作成する処理の流れとしては、

  1. ボタンを押すと処理が開始される(今回はダミー処理)
  2. 処理が進行していく(この間、アニメが再生される)
  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周りのものです。

参考

コチラを参考にしました。ありがとうございました!

ferret-plus.com

www.pixelimage.jp

www.r-nakai.com

bbs.wankuma.com

hensa40.cutegirl.jp

あとがき

C#のWindows Formsアプリで、処理中にクルクルするローディングアニメーションを作る方法についてでした!

スレッド的な頭が必要かもしれないので、はじめは微妙にとっつきにくいかも知れません。

別スレッド的に処理を作らないといけないので、既存処理に組み込む場合は、多少なりともインパクトがあると思うのでご注意くださいませ。

気になった方は是非試してみてください!