プログラミングネタ探しをして、Webを徘徊していると、C#(Formベース)のコントロール一覧のページがあり、そこに

 

BackgroundWorkクラス

 

が出ていた。

 

なんだ、これ???

 

ということで調べてみると、ボタンやラベル等のよくあるUIのWindows Controlではなく、時間を要する処理を行う為の(別)スレッドユーティリティのようです。これはまだ試したことがないので、関連サイトを調べ、似たようなサンプルを参考に、(実に久しぶりに)"InitializeCOmponent"を使わない、MSCompAss用の「全コード」サンプルを作ってみました。

 

細かい解説は↓のサンプルのコードのコメントを参照されたいのですが、大まかなポイントは、

 

(1)参照DLLとして"C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.ComponentModel.EventBasedAsync\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.ComponentModel.EventBasedAsync.dll"を加え、コードには"using System.ComponentModel;"を追加します。

(2)System.ComponentModel.BackgroundWorkerのインスタンスを作成します。この際に、

         DoWork
         ProgressChanged
         RunWorkerCompleted

という3つのイベントにメソッドを追加しておきます。

(3)RunWorkerAsyncメソッドで別スレッドの処理(DoWorkイベントで追加したメソッド)を開始します。進捗はProgressChangedイベントのProgressChangedEventArgs eで報告され、完了時に終了の仕方がのRunWorkerCompletedイベントのRunWorkerCompletedEventArgs eで確認できます。

 

以下のサンプルでは、お約束の0.5秒Sleepを10回繰り返すだけですが、実際の処理では完了までの時間、処理量を100として、0から定量化する必要があるようです。ネットワークからのダウンロード等、環境で処理時間が変わり、進捗が変更されるものもありますが、そこは(ユーザーサイドで)ロジックを組む必要がある、ということだと思います。

 

ご参考まで。

 

【BackgroundWorkSample.cs】

//-------------------------
// BackgroundWorker Sample
//-------------------------

using System;
using System.Drawing;
using System.Windows.Forms;
using System.ComponentModel;    //BackgroundWorkerを使う為
using System.Reflection;        //Assemblyを使う為

namespace BWSample
{
    ///////////////////////////
    //エントリーポイントクラス
    ///////////////////////////

    class MainApp
    {
        [STAThread]
        public static void Main()
        {
            Application.Run(new MainForm());
        }
    }

    public partial class MainForm : Form
    {
        //メンバーコントロール
        BackgroundWorker backgroundWorker;    //但しMainFormの子になることはない
        Label resultLabel;
        ProgressBar    progressBar;
        Button startAsyncBtn;
        Button cancelAsyncBtn;

        public MainForm()
        {
            InitControls();                    //MainFormとコントロールの初期化
            backgroundWorker.WorkerReportsProgress = true;        //進行状況の更新を報告可否
            backgroundWorker.WorkerSupportsCancellation = true;    //キャンセルのサポート可否
        }

        //BWFormとコントロールの初期化
        private void InitControls()
        {
            //フォームの初期化
            Assembly myOwn = Assembly.GetEntryAssembly();
            this.Icon = Icon.ExtractAssociatedIcon(myOwn.Location);    //プログラムアイコンをフォームにつける
            this.Text = "BackgropundWorker Sample";
            this.ClientSize = new Size(480, 80);
            this.MinimumSize = new Size(480, 80);
            this.BackColor = SystemColors.Window;
            //ボタンの初期化
            startAsyncBtn = new Button();
            startAsyncBtn.Location = new Point(ClientSize.Width - startAsyncBtn.Width - 10, 10);
            startAsyncBtn.Text = "開始";
            startAsyncBtn.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
            startAsyncBtn.Click += startAsyncBtn_Click;
            this.Controls.Add(startAsyncBtn);
            cancelAsyncBtn = new Button();
            cancelAsyncBtn.Location = new Point(ClientSize.Width - cancelAsyncBtn.Width - 10, startAsyncBtn.Height + 20);
            cancelAsyncBtn.Text = "中止";
            cancelAsyncBtn.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
            cancelAsyncBtn.Click += cancelAsyncBtn_Click;
            this.Controls.Add(cancelAsyncBtn);
            //ラベルの初期化
            resultLabel = new Label();
            resultLabel.Location = new Point(10, 10);
            resultLabel.Anchor = (AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Left);
            resultLabel.BorderStyle = BorderStyle.Fixed3D;
            resultLabel.Width = this.ClientSize.Width - startAsyncBtn.Width - 30;
            resultLabel.Text = "ここに進捗を表示する";
            this.Controls.Add(resultLabel);
            //プログレスバーの初期化
            progressBar = new ProgressBar();
            progressBar.Location = new Point(10, startAsyncBtn.Height + 20);
            progressBar.Anchor = (AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Left);
            progressBar.Width = this.ClientSize.Width - startAsyncBtn.Width - 30;
            progressBar.Minimum = 0;
            progressBar.Maximum = 100;
            this.Controls.Add(progressBar);
            //BackgroundWorkerの初期化
            backgroundWorker = new BackgroundWorker();
            backgroundWorker.DoWork += backgroundWorker_DoWork;  //別スレッド処理
            backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker_ProgressChanged);  //進捗変化があった場合の処理
            backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker_RunWorkerCompleted);  //処理が完了した場合の処理
            /* UIコントロールの様にControls.Addによって子供にする必要はない */
        }

        //startAsyncBtnを押した場合の処理
        private void startAsyncBtn_Click(object sender, EventArgs e)
        {
            if(backgroundWorker.IsBusy != true)
            {
                //非同期処理開始
                backgroundWorker.RunWorkerAsync();
            }
        }

        //cancelAsyncBtnを押した場合の処理
        private void cancelAsyncBtn_Click(object sender, EventArgs e)
        {
            if(backgroundWorker.WorkerSupportsCancellation == true)
            {
                //非同期処理キャンセル
                backgroundWorker.CancelAsync();
            }
        }

        //BackgroundWorkerによるバックグラウンド処理
        private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;    //"as"はobjectのキャスト演算子
            for(int i = 1; i <= 10; i++)
            {
                if(worker.CancellationPending == true)    //キャンセルされた場合
                {
                    e.Cancel = true;
                    break;
                }
                else
                {
                    //時間のかかる処理をここで行う
                    System.Threading.Thread.Sleep(500);
                    worker.ReportProgress(i * 10);    //MinimumとMaximumに合致するように設定する
                }
            }
        }

        //バックグラウンド進捗処理
        private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            resultLabel.Text = (e.ProgressPercentage.ToString() + "%が処理されました。");
            //プログレスバーの値を変更する
            if(e.ProgressPercentage < this.progressBar.Minimum)
            {
                this.progressBar.Value = this.progressBar.Minimum;
            }
            else if(this.progressBar.Maximum < e.ProgressPercentage)
            {
                this.progressBar.Value = this.progressBar.Maximum;
            }
            else
            {
                this.progressBar.Value = e.ProgressPercentage;
            }
        }

        //バックグラウンド終了処理
        private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if(e.Cancelled == true)
            {
                resultLabel.Text = "中止されました。";
            }
            else if(e.Error != null)
            {
                resultLabel.Text = "エラー:"  + e.Error.Message;
            }
            else
            {
                resultLabel.Text = "完了しました。";
            }
        }
    }
}