redwarrior’s diary

C#, ASP.NET, WEB, G*などの雑多な情報

ProgressBarの表示をMVVMアーキテクチャで実装する

ProgressBarの表示って、非同期処理が入るので、意外と難しいですよね。

表示の仕方は、以下のサイトで分かりやすく説明されているのですが、素のWPFなのです。

anderson02.com

どうせならば、MVVMアーキテクチャで実装したいなと思いやってみました。

まず、Visual Studioに「Prism Template Pack拡張機能をインストールします。

次に、「Prism Blank App」プロジェクトテンプレートを使用して、プロジェクトを作成します。

そして、ProgressBarコントロールと実行ボタンをXAMLに書きます。

MainWindow.xaml

<Window x:Class="PrismProgressBarApp3.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:prism="http://prismlibrary.com/"
        prism:ViewModelLocator.AutoWireViewModel="True"
        Title="{Binding Title}" Height="350" Width="525">
    <Grid>
        <StackPanel Orientation="Horizontal" Grid.Row="0">
            <ProgressBar Height="30" Width="300" Margin="80,0,0,0"
                         Minimum="0" Maximum="100"
                         Value="{Binding ProgressValue}" />
            <Button Height="30" Width="60" Margin="30,0,0,0" Content="Run"
                    Command="{Binding RunCommand}" />
        </StackPanel>
    </Grid>
</Window>

さらに、ProgressBarとBindingする値や、Runボタンをクリックした時のコマンドをViewModelに作成します。

MainWindowViewModel.cs

using Prism.Commands;
using Prism.Mvvm;

namespace PrismProgressBarApp3.ViewModels
{
    public class MainWindowViewModel : BindableBase
    {
        private string _title = "Prism Application";
        private int _progressValue;

        public string Title
        {
            get => _title;
            set => SetProperty(ref _title, value);
        }

        public int ProgressValue
        {
            get => _progressValue;
            set => SetProperty(ref _progressValue, value);
        }

        public DelegateCommand RunCommand { get; set; }

        public MainWindowViewModel()
        {
            RunCommand = new DelegateCommand(ExecuteMethod);
        }

        private void ExecuteMethod()
        {
            throw new System.NotImplementedException();
        }
    }
}

さて、これでExecuteMethodメソッドで、ProgressValueプロパティの値を増やす処理を書けば進捗状況が更新されていきます。

ただし、上記サイトの例でもそうですが、ProgressValueの値の更新は非同期に行う必要があります。さらに、非同期処理の中では、コントロールを操作できないため、UIスレッドで変更する必要がありました。

しかし、MVVMアーキテクチャで実装すると、片方の問題は気にしなくて済みました。

private void ExecuteMethod()
{
    Task.Run(() =>
    {
        for (var i = 0; i < 10; i++)
        {
            Thread.Sleep(500);
            ProgressValue += 10;
        }
    });
}

Task.Run()で別スレッドにするのですが、データバインディングによるUIへの反映がUIスレッドで行われるため、Dispatcherを使う必要がなく、簡単に作成できました。

最後に、スリープのためにTask.Delay()を使ったりすると、await/asyncの知識が必要になるため、今回の用途ではThread.Sleep()で十分だと思います。

なお、ソースコードは、GitHubで公開しています。

github.com

動作環境

以上