バックグラウンドで処理を行う


今回はWindowsPhone7でバックグラウンド処理を行う方法とプログレスバーの使い方を紹介します。

アプリの開発者はバックグラウンドで処理を行わないと行けない場面がたくさんあります。
WindowsPhone7ではネットワーク処理を行うことや大きな画像の縮小処理など時間のかかる処理をUIスレッドで行うのはご法度です。
そういった場合にはバックグラウンドで処理を行い、処理が終了した時にUIスレッドで結果を反映する処理を行います。

しかし単純に時間のかかる処理をバックグラウンドで処理で行うのでは表示内容がかわらないためユーザはアプリが動いていないと感じ不快に思うことがあります。
そのためバックグラウンドで処理を行う場合には出来る限りユーザにアプリが動作していることを知らせる必要があります。

そこで今回は、バックグラウンドの処理を行うBackgroundWorkerを紹介とバックグラウンドの処理の進行状況を表示するProgressBarを紹介します。

BackgroundWorkerでは下記の実装が必要となります。

  1. 初期化
  2. バックグラウンド処理の実装
  3. バックグラウンド終了処理の実装
  4. バックグラウンド処理の進捗状況を取得処理の実装

詳しい内容は続きを御覧ください。

BackgroundWorker

初期化

まずはBackgroundWorkerの初期化を行います。
BackgroundWorkerは下記のメソッドを用いてバックグラウンドの実行を行います。

public void RunWorkerAsync ();

BackgroundWorkerのRunWorkerAsyncを行うことでバックグラウンドの処理を行うことができますが、RunWorkerAsyncを行う前に必ずバックグラウンドの処理を行うメソッドとバックグラウンドの処理が完了した時に呼ばれるメソッドを設定しましょう。
必要と有らば進捗状況の取得メソッドも設定します。
下記のように記述します。

BackgroundWorker background = new BackgroundWorker();
background.DoWork += new DoWorkEventHandler(doBackgroundWork);
background.RunWorkerCompleted += new RunWorkerCompletedEventHandler(completedBackgroundWork);
background.ProgressChanged += new ProgressChangedEventHandler(progressChangedBackgroundWork);
background.WorkerReportsProgress = true;
background.RunWorkerAsync();

2行目のDoWorkではバックグラウンド処理を開始した時に呼び出されるメソッドをdoBackgroundWorkに設定しています。
3行目のRunWorkerCompletedではバックグラウンドの処理が完了した際にcompletedBackgroundWorkメソッドを呼び出すように設定してます。
4行目のProgressChangedはDoWorkで設定したメソッドでReportProgressメソッドが呼ばれた場合に非同期で呼び出されるメソッドをprogressChangedBackgroundWorkに設定しています。

ここで注意しなければならないのはProgressChangedのイベントを行う場合はBackgoroundWorkerのプロパティWorkerReportsProgressをtrueにするようにしてください。
WorkerReportsProgressをfalse(未設定時はfalse)のままの状態でReportProgressを実行した場合はエラーになります。

バックグラウンド処理の実装

バックグラウンド処理では初期化時にDoWorkで設定したメソッドを下記のように実装します。
例えばdoBackgroundWorkというメソッドを実装する場合は下記のようになります。

        void doBackgroundWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = (BackgroundWorker)sender;
            /* 重たい処理 (省略)*/
            worker.ReportProgress(【適当な値】);
        }

DoWorkで設定したメソッドで処理しているときに下記のメソッドをでProgressChangedイベントを発行することができます。

public void ReportProgress (int percentProgress);

ReportProgressの引数で与えた値をProgressChangedイベントのコールバックで取得することができます。

バックグラウンド終了処理の実装

DoWorkで設定したメソッドが終了した際にRunWorkerCompletedで設定したメソッドが呼び出されます。
ここで呼び出されるメソッドはUIスレッドより呼び出されるのでUIの操作が可能です。
例としてcompletedBackgroundWorkというメソッドを実装する場合は下記のようになります。

    void completedBackgroundWork(object sender, RunWorkerCompletedEventArgs e)
    {
        /*スプラッシュ画面を消すなどのUIの操作*/
    }

バックグラウンド処理の進捗状況を取得処理の実装

DoWorkで設定したメソッドでProgressChangedイベントが発行された場合に設定したコールバックのメソッドが呼び出されます。
例としてコールバックのメソッドがprogressChangedBackgroundWorkの場合下記のようになります。

    private void progressChangedBackgroundWork(object sender, ProgressChangedEventArgs e)
    {
        int value = e.ProgressPercentage;
    }

ProgressChangedEventArgsクラスのProgressPercentageにてReportProgressの引数で与えた値を取得することができます。

ProgressBarの表示

xamlファイルに下記を埋め込みます。

<ProgressBar x:Name="progressBar" IsIndeterminate="True" Width="300" Height="30" Margin="104,621,76,149"/>

ProgressBarの設定内のIsIndeterminateをTrueに設定している場合は下図のように左から右へ点が流れていくアニメーションが表示されます。

IsIndeterminateをFalseに設定し0~100の間の値を設定すると下図ようにバーを表示することができます。

バーの初期値を70と設定する場合は下記のようになります

<ProgressBar x:Name="progressBar" IsIndeterminate="False" Width="300" Height="30" Margin="104,621,76,149" Value="70"  />

そしてプログレスバーの値を変更する場合は下記のようにプロパティのValueの値を変更することでできます。

   progressBar.Value = 30;

以上までの内容をまとめたものをスプラッシュ画面を表示する方法のサンプルに加えました。
処理の流れは下図のようになります。

スプラッシュ画面を表示する方法の記事では一定時間が経てばスプラッシュ画面を消していましたが今回のサンプルではバックグラウンドの処理が終わればスプラッシュ画面を消すようにしています。
■MainPage.xaml

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <Button Content="スプラッシュ画面表示" Height="601" HorizontalAlignment="Left" Margin="6,6,0,0" Name="button1" VerticalAlignment="Top" Width="444" Click="button_Click" />
</Grid>

■MainPage.xaml.cs

namespace SplashApp
{
    public partial class MainPage : PhoneApplicationPage
    {
        private Popup popup;
        private BackgroundWorker background;
        private PopupSplash popupView;

        // コンストラクター
        public MainPage()
        {
            InitializeComponent();

            showSplash();
        }

        private void button_Click(object sender, RoutedEventArgs e)
        {
            showSplash();
        }


        void showSplash()
        {
            this.popup = new Popup();
            popupView = new PopupSplash();
            this.popup.Child = popupView;
            this.popup.IsOpen = true;
            StartLoad();
        }

        void dismissSplash()
        {
            this.popup.IsOpen = false;
        }

        private void StartLoad()
        {
            background = new BackgroundWorker();
            background.DoWork += new DoWorkEventHandler(doBackgroundWork);
            background.ProgressChanged += new ProgressChangedEventHandler(progressChangedBackgroundWork);
            background.RunWorkerCompleted += new RunWorkerCompletedEventHandler(completedBackgroundWork);
            background.WorkerReportsProgress = true;
            background.RunWorkerAsync();
        }

        void completedBackgroundWork(object sender, RunWorkerCompletedEventArgs e)
        {
            this.popup.IsOpen = false;
        }

        void doBackgroundWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = (BackgroundWorker)sender;
            for (int i = 0; i < 4; i++)
            {
                /* 重たい処理 (省略)*/
                worker.ReportProgress(25 * i);
            }
        }

        private void progressChangedBackgroundWork(object sender, ProgressChangedEventArgs e)
        {
            popupView.changeProgressValue(e.ProgressPercentage);
        }
    }
}

■PopupSplash.xaml

<Grid x:Name="LayoutRoot" Background="White" Width="480" Height="800">
    <Image Height="800" HorizontalAlignment="Left" Name="image1" Stretch="Fill" VerticalAlignment="Top" Width="480" Source="/SplashScreenImage.jpg" />
    <ProgressBar x:Name="progressBar" IsIndeterminate="False" Width="300" Height="30" Margin="104,621,76,149" Value="0"  />
</Grid>

■PopupSplash.xaml.cs

namespace SplashApp
{
    public partial class PopupSplash : UserControl
    {
        public PopupSplash()
        {
            InitializeComponent();
        }

        public void changeProgressValue(int value)
        {
            progressBar.Value = value;
        }
    }
}

このサンプルを実行すると下図のようにスプラッシュ画面が表示され、doBackgroundWorkの処理が終わるとスプラッシュ画面を消します。

表示が終わると下図のような画面が表示され中央のボタンを押すと再度スプラッシュ画面を表示することができます。