DataGridのカラムを動的に作成する

 通常DataGridのカラム設定はXAMLで行います。
 カラムの設定では表示する値をデータバインディングで指定します。
 データバインディングしたプロパティがコレクションの場合はどうなるでしょうか?
 DataGridの自動カラム生成機能を利用して作成してみた結果が下の図です。
DataGrid自動生成
 カラムを自動生成した場合はヘッダー名はプロパティ名になります。
 右側の2つはLivetのViewModelクラスに実装されているプロパティですので今回は関係ありません。
 問題はValuesプロパティです。
 DataGridには(コレクション)とだけ表示されています。
 データテンプレートを使用して1つのセルに複数の値を表示することは可能ですが、コレクションの中身を展開してそれぞれ別カラムとして表示することはできません。
 今回の目的はコレクション(Values)の中身を展開し、それぞれ別カラムとして表示することです。
 いくつか方法はあるのでしょうが、この記事ではコードビハインド(C#)で動的にDataGridのカラムを定義します。

 まず表示するデータについて説明します。
 今回は例として以下の様なクラスを用意しました。
 DataGridのカラムを動的に作成するためのサンプルなので、ViewModelにModelの役割も持たせています。
using Livet;

namespace DataGridDynamicColumn.ViewModels
{
    public class DataViewModel : ViewModel
    {
        public DataViewModel()
        {
            Value = "";
            BackgroundColor = "";
        }

        #region Value変更通知プロパティ
        private string _Value;

        public string Value
        {
            get
            { return _Value; }
            set
            { 
                if (_Value == value)
                    return;
                _Value = value;
                RaisePropertyChanged();
            }
        }
        #endregion


        #region BackgroundColor変更通知プロパティ
        private string _BackgroundColor;

        public string BackgroundColor
        {
            get
            { return _BackgroundColor; }
            set
            { 
                if (_BackgroundColor == value)
                    return;
                _BackgroundColor = value;
                RaisePropertyChanged();
            }
        }
        #endregion

    }
}
 DataViewModelクラスは1件のデータを表すクラスです。
 値とDataGridで表示する際の背景色を持っています。
using Livet;
using System.Collections.ObjectModel;

namespace DataGridDynamicColumn.ViewModels
{
    public class ResultViewModel : ViewModel
    {
        public ResultViewModel(string name)
        {
            Name = name;
            Values = new ObservableCollection<DataViewModel>();
        }

        #region Name変更通知プロパティ
        private string _Name;

        public string Name
        {
            get
            { return _Name; }
            set
            { 
                if (_Name == value)
                    return;
                _Name = value;
                RaisePropertyChanged();
            }
        }
        #endregion


        public ObservableCollection<DataViewModel> Values { get; private set; }
    }
}
 ResultViewModelクラスは1つのテスト結果を保持するためのクラスです。
 テスト名を表すNameプロパティと各サンプルのテスト結果のコレクション(Values)を持っています。
using Livet;
using System.Collections.ObjectModel;

namespace DataGridDynamicColumn.ViewModels
{
    public class MainWindowViewModel : ViewModel
    {
        public MainWindowViewModel()
        {
            HeaderNames = new[] { "テスト名", "サンプル1", "サンプル2", "サンプル3" };
            Items = new ObservableCollection<ResultViewModel>();
            var result1 = new ResultViewModel("テスト1");
            result1.Values.Add(new DataViewModel() { Value = "A" });
            result1.Values.Add(new DataViewModel() { Value = "B" });
            result1.Values.Add(new DataViewModel() { Value = "C" });
            Items.Add(result1);

            var result2 = new ResultViewModel("テスト2");
            result2.Values.Add(new DataViewModel() { Value = "B" });
            result2.Values.Add(new DataViewModel() { Value = "D" });
            result2.Values.Add(new DataViewModel() { Value = "A" });
            Items.Add(result2);

            var result3 = new ResultViewModel("テスト3");
            result3.Values.Add(new DataViewModel() { Value = "C" });
            result3.Values.Add(new DataViewModel() { Value = "A" });
            result3.Values.Add(new DataViewModel() { Value = "A" });
            Items.Add(result3);
        }

        public string[] HeaderNames { get; private set; }

        public ObservableCollection<ResultViewModel> Items { get; private set; }

        // Valueの値を元に色分けする
        public void ColorCoding()
        {
            foreach(var result in Items)
            {
                foreach(var data in result.Values)
                {
                    switch (data.Value)
                    {
                        case "A":
                            data.BackgroundColor = "Green";
                            break;
                        case "B":
                            data.BackgroundColor = "LightGreen";
                            break;
                        case "D":
                            data.BackgroundColor = "Red";
                            break;
                        default:
                            break;
                    }
                }
            }
        }

    }
}
 MainWindowViewModelでは表示するデータを作成しています。
 ColorCodingメソッドではデータの値を使用して背景色を指定するメソッドです。
 次にViewを作成します。
<Window x:Class="DataGridDynamicColumn.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
        xmlns:l="http://schemas.livet-mvvm.net/2011/wpf"
        xmlns:v="clr-namespace:DataGridDynamicColumn.Views"
        xmlns:vm="clr-namespace:DataGridDynamicColumn.ViewModels"
        Title="MainWindow" SizeToContent="WidthAndHeight">
    
    <Window.DataContext>
        <vm:MainWindowViewModel/>
    </Window.DataContext>
    
    <i:Interaction.Triggers>
        <!--Windowが閉じたタイミングでViewModelのDisposeメソッドが呼ばれます-->
        <i:EventTrigger EventName="Closed">
            <l:DataContextDisposeAction/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
    
    <Grid x:Name="RootGrid">
        <Grid.Resources>
            <Style x:Key="HeaderStyle" TargetType="{x:Type DataGridColumnHeader}">
                <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                <Setter Property="TextBlock.TextAlignment" Value="Center"/>
            </Style>
            
            <Style x:Key="TextStyle" TargetType="{x:Type TextBlock}">
                <Setter Property="HorizontalAlignment" Value="Center"/>
            </Style>
        </Grid.Resources>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        
        <DataGrid Grid.Row="0" Name="ResultDataGrid" ItemsSource="{Binding Items}" AutoGenerateColumns="False">
            
        </DataGrid>
        
        <Button Grid.Row="1" Content="色分けを行う">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="Click">
                    <l:LivetCallMethodAction MethodTarget="{Binding}" MethodName="ColorCoding"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </Button>
    </Grid>
</Window>
 DataGridと色分けボタンがあるだけの簡単なViewです。
 今回、DataGridのカラム設定を動的に行うのでXAMLではカラム定義を行っていません。
 DataGridではItemsSourceのデータバインディングとAutoGenerateColumnsをOffにしているだけです。
 最後に、コードビハインドでDataGridのカラム設定を行います。
using DataGridDynamicColumn.ViewModels;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace DataGridDynamicColumn.Views
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            InitializeDataGrid();
        }

        private void InitializeDataGrid()
        {
            var viewModel = (MainWindowViewModel)DataContext;

            // リソースからスタイルを取得
            var headerStyle = (Style)RootGrid.FindResource("HeaderStyle");
            var textStyle = (Style)RootGrid.FindResource("TextStyle");

            // テスト名カラムを設定
            ResultDataGrid.Columns.Add(new DataGridTextColumn()
            {
                Header = viewModel.HeaderNames.First(),
                HeaderStyle = headerStyle,
                ElementStyle = textStyle,
                IsReadOnly = true,
                CanUserSort = true,
                Binding = new Binding("Name")
            });

            // ResultViewModelのValuesの中身をカラムに設定する
            var headerNames = viewModel.HeaderNames.Skip(1).ToArray();
            for(var i = 0; i < headerNames.Length; i++)
            {
                // スタイルを作成
                var propertyBaseName = string.Format("Values[{0}]", i);
                var style = new Style(typeof(DataGridCell));
                style.Setters.Add(new Setter(
                    DataGridCell.BackgroundProperty, 
                    new Binding(propertyBaseName + ".BackgroundColor")));
                style.Setters.Add(new Setter(
                    DataGridCell.MarginProperty,
                    new Thickness(0)));

                // データ表示用カラムを作成
                ResultDataGrid.Columns.Add(new DataGridTextColumn()
                {
                    Header = headerNames[i],
                    HeaderStyle = headerStyle,
                    ElementStyle = textStyle,
                    IsReadOnly = true,
                    CanUserSort = true,
                    Binding = new Binding(propertyBaseName + ".Value"),
                    CellStyle = style
                });
            }
        }
    }
}
 DataGridのカラムを動的に作成するのは意外と簡単で、目的のDataGridのColumnsにデータグリッドカラムを追加するだけです。
 データグリッドカラムには幾つか種類がありますが、今回はテキスト表示するだけなのでDataGridTextColumnを使用しています。
 DataGridTextColumnのプロパティにヘッダー名やスタイル等を指定します。
 カラムのヘッダーを定義するためにはHeaderプロパティにヘッダーに表示するテキストを、HeaderStyleにヘッダーのスタイルを指定します。
 サンプルコードではXAMLでStyle定義を行い、FindResourceメソッドでそのスタイルを取得しています。
 スタイルを動的に作成する必要がない場合はこちらの方が書き易いと思います。
 IsReadOnlyプロパティで値が編集できるかどうかを指定できます。
 CanUseSortプロパティをtrueにするとDataGridのヘッダーをクリックするだけで表の並び替えが行えます。
 表示する値の定義はElementStyleプロパティ、CellStyleプロパティ、Bindingプロパティで行います。
 ElementStyleプロパティにはセル内に表示する要素のスタイルを指定します。
 CellStyleプロパティにはDataGridのセルのスタイルを指定します。(ヘッダーでは省略しています)
 Bindingプロパティで表示する値とデータバインディングを行います。
 Bindingクラスのコンストラクタにデータバインディングしたいプロパティ名のPathを指定するだけです。

 基本的にはこんな感じでカラム設定を行っていきます。
 Valuesの中身を展開し、それぞれ別カラムとして定義する場合もほぼ同じです。
 異なる点はデータバインディングするプロパティのパスを動的に作成して指定する必要がある点です。
 今回の場合、Valueプロパティのデータバインディングを行う場合はValues[0].Valueの様にPathを指定します。
 また、Styleでデータバインディングを使用したい場合はXAMLで定義することはできません。(プロパティのPathがValue[x].Valueの様になり、XAMLで指定できないので)
 データバインディングを使用したStyleを使用する場合はStyleも動的に作成する必要があります。
 Styleの定義はコンストラクタでデータタイプを指定します。
 今回作成するのはDataGridCellのスタイルなので型にDataGridCellを指定します。
 あとはSettersにスタイルの条件を追加していきます。
 Setterコンストラクタの第1引数にプロパティを、第2引数に値を指定します。
 データバインディングしたい場合は、第2引数にBindingインスタンスを指定します。

 このコードを実行すると以下の様になります。
動的DataGrid1
 目論見通りValuesの中身が展開されて別カラムとして表示されています。
 「色分けを行う」ボタンを押すと各データのBackgroundColorが指定され、セルの背景色が変化します。
動的DataGrid2
 今回はテキスト表示だけだったのでDataGridTextColumnを使用しましたが、DataGridCheckBoxColumn等を使用する場合も同じです。
 ただし、DataGridTemplateColumnは他と使い方が違う為、同じ方法ではうまくいきません。(DataGridTemplateColumnはBindingプロパティを持たないので)
 DataGridTemplateColumnを使用する場合は、Bindingではなくデータテンプレートでセルの内容を定義する必要があります。
 DataGridTemplateColumnは使ったことがないので説明は省略します。(Styleと同じ用にプロパティのPathに注意してデータテンプレートを作成すればうまくいくと思います)
スポンサーサイト

テーマ : プログラミング
ジャンル : コンピュータ

Rxを使って並列処理時の状況通知を行ってみる

 以前「asyncとIProgressを使ってプログレスバーを操作する」で、状況通知を行う方法を紹介しました。
 この時は重たい処理をUIスレッドとは別のスレッドで実行していますが、処理自体はシングルスレッドで行っていました。
 今回は重たい処理を並列処理で行う場合のプログレスバーによる状況通知をやってみます。(キャンセル機能は省略)
 基本的な考え方は、「asyncとIProgressを使ってプログレスバーを操作する」と同じです。
 違う点は並列処理を行うため、現在の作業状況を更新する際には排他制御が必要になる点です。
 後はおまけとしてRxを利用して状況通知回数を減らし、プログレスバー更新にかかる負荷を減らしてみようと思います。
 先ずは状況通知を行うためのクラスを定義します。
using System;
using System.Reactive.Linq;
using System.Reactive.Subjects;

namespace ParallelProgress.Models
{
    public class ProgressInfo
    {
        public ProgressInfo(int value, int total)
        {
            Value = value;
            Total = total;
        }

        public int Value { get; set; }

        public int Total { get; set; }
    }

    public class SamplingProgress<TInfo> : IDisposable
    {
        private IProgress<TInfo> Progress;

        private Subject<TInfo> ProgressSubject;
        private IDisposable Disposer;

        /// <summary>
        /// サンプリングプログレスを作成する。
        /// </summary>
        /// <param name="handler">状況通知ハンドラ</param>
        /// <param name="interval">サンプリングインターバル(ms)</param>
        public SamplingProgress(Action<TInfo> handler, int interval = 500)
        {
            if (handler == null) throw new ArgumentNullException("handler");

            Progress = new Progress<TInfo>(handler);
            ProgressSubject = new Subject<TInfo>();
            Disposer = ProgressSubject
                .Sample(TimeSpan.FromMilliseconds(interval))
                .Subscribe(i => Progress.Report(i));
        }

        /// <summary>
        /// 進行状況の更新を報告する。
        /// </summary>
        /// <param name="info">進行状況</param>
        /// <param name="isSampling">サンプリングを行うか否か</param>
        public void Report(TInfo info, bool isSampling = true)
        {
            if (info == null) throw new ArgumentNullException("info");
            if (_Disposed) throw new ObjectDisposedException("SamplingProgress");

            if(isSampling)
            {
                ProgressSubject.OnNext(info);
            }
            else
            {
                // サンプリングせずに報告する。
                Progress.Report(info);
            }
        }

        protected bool _Disposed = false;

        /// <summary>
        /// サンプリングを停止する。
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if(!_Disposed)
            {
                if(disposing)
                {

                }

                ProgressSubject.OnCompleted();
                Disposer.Dispose();
                ProgressSubject.Dispose();
                Progress = null;
                _Disposed = true;
            }
        }
    }
}
 ProgressInfoには全体の処理工程数(Total)と現在の値(Value)のみ持たせています。(プログレスバーの更新に必要な情報のみ持たせました。)
 ParallelProgress<TInfo>は、Rxを利用して無駄な状況通知を行わないようにした状況通知クラスです。
 RxにあるSampleメソッドを利用して無駄な状況通知を除外しています。
 Sampleメソッドは指定した時間内に流れてきた値を集め(サンプリングし)最後の値のみ次に通すメソッドです。
 Sampleメソッドを使用して、ほぼ同タイミングで行われる複数のプログレスバー更新を1回に圧縮しています。
 Reportメソッドでは引数でRxによるサンプリングを行うかどうかを制御できるようにしてあります。
 isSamplingにfalseを渡すことで即座にプログレスバーを更新できます。
 Disposeした場合(OnCompletedした場合)、サンプリング途中だった状況通知は破棄されてしまいます。
 そのため最後の通知はサンプリングなしで状況通知する必要があります。

 次にModelを作成します。
 Modelでは重たい処理を非同期で行います。
using Livet;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace ParallelProgress.Models
{
    public class Model : NotificationObject
    {
        public Model()
        {
            IsSampling = true;
        }

        #region IsSampling変更通知プロパティ
        private bool _IsSampling;
        /// <summary>
        /// 状況報告時にサンプリングするかどうかを設定、又は取得する。
        /// </summary>
        public bool IsSampling
        {
            get
            { return _IsSampling; }
            set
            { 
                if (_IsSampling == value)
                    return;
                _IsSampling = value;
                RaisePropertyChanged();
            }
        }
        #endregion

        /// <summary>
        /// 重たい処理を開始する。
        /// </summary>
        /// <param name="handler">状況報告ハンドラ</param>
        /// <returns></returns>
        public Task HeavyWorkAsync(Action<ProgressInfo> handler)
        {
            if (handler == null) throw new ArgumentNullException("handler");

            var values = Enumerable.Range(1, 500).ToArray();
            var pOption = new ParallelOptions() { MaxDegreeOfParallelism = Environment.ProcessorCount };
            int progressValue = 0;
            int progressTotal = values.Length;

            return Task.Run(() =>
            {
                using (var progress = new SamplingProgress<ProgressInfo>(handler))
                {
                    Parallel.ForEach(values, pOption, value =>
                    {
                        Task.Delay(50).Wait();

                        Interlocked.Increment(ref progressValue);
                        progress.Report(new ProgressInfo(progressValue, progressTotal), IsSampling);
                    });

                    // 最後の状況通知を100%で終わらせるための通知。
                    progress.Report(new ProgressInfo(progressValue, progressTotal), false);
                }
            });
        }
    }
}
 並列処理はParallel.ForEachで行いました。
 ParallelOptionsを使用することで並列処理時の最大スレッド数を制御しています。(プロセッサ数を上限としています。)
 もしキャンセル機能を組み込みたい場合は、ParallelOptionsのCancellationToken にキャンセルトークンを指定し、try-catch文でOperationCanceledExceptionを捕まえてください。
 並列処理を行っているので現在の処理数(progressValue)を更新する際は排他制御を行う必要があります。
 今回はlock文ではなくSystem.Threading.Interlockedクラスを使用して排他制御を行いました。
 Interlockedクラスを利用すると値のインクリメント・デクリメントや加算・減算をシンプルかつ低コストで記述できます。

 あとはViewModelとViewを作成します。
using Livet;
using Livet.EventListeners;

using ParallelProgress.Models;

namespace ParallelProgress.ViewModels
{
    public class MainWindowViewModel : ViewModel
    {
        private Model Model;

        public MainWindowViewModel()
        {
            Model = new Model();

            IsEnabled = true;
            ProgressValue = 0;
            ProgressTotal = 1;
            LogMessageList = new DispatcherCollection<string>(DispatcherHelper.UIDispatcher);

            CompositeDisposable.Add(new PropertyChangedEventListener(Model)
            {
                (sender, e) => RaisePropertyChanged(e.PropertyName)
            });
        }

        /// <summary>
        /// 作業状況のログメッセージ
        /// </summary>
        public DispatcherCollection<string> LogMessageList { get; private set; }

        /// <summary>
        /// 作業状況報告時にサンプリングするかどうか。
        /// </summary>
        public bool IsSampling
        {
            get { return Model.IsSampling; }
            set { Model.IsSampling = value; }
        }

        #region ProgressValue変更通知プロパティ
        private int _ProgressValue;
        /// <summary>
        /// 現在の処理状況を取得する。
        /// </summary>
        public int ProgressValue
        {
            get
            { return _ProgressValue; }
            private set
            { 
                if (_ProgressValue == value)
                    return;
                _ProgressValue = value;
                RaisePropertyChanged();
            }
        }
        #endregion


        #region ProgressTotal変更通知プロパティ
        private int _ProgressTotal;
        /// <summary>
        /// 処理の全工程数を取得する。
        /// </summary>
        public int ProgressTotal
        {
            get
            { return _ProgressTotal; }
            private set
            { 
                if (_ProgressTotal == value)
                    return;
                _ProgressTotal = value;
                RaisePropertyChanged();
            }
        }
        #endregion


        #region IsEnabled変更通知プロパティ
        private bool _IsEnabled;
        /// <summary>
        /// 処理開始ボタンのIsEnabledを取得する。
        /// </summary>
        public bool IsEnabled
        {
            get
            { return _IsEnabled; }
            private set
            { 
                if (_IsEnabled == value)
                    return;
                _IsEnabled = value;
                RaisePropertyChanged();
            }
        }
        #endregion

        /// <summary>
        /// 処理を開始する。
        /// </summary>
        public async void HeaveyWorkAsync()
        {
            IsEnabled = false;
            LogMessageList.Clear();

            await Model.HeavyWorkAsync(info =>
            {
                ProgressTotal = info.Total;
                ProgressValue = info.Value;

                var message = string.Format("作業状況:{0}/{1}", info.Value, info.Total);
                LogMessageList.Add(message);
            });

            IsEnabled = true;
        }
    }
}
 ViewModelでは状況通知のサンプリングの有無を解りやすくするために状況通知ログメッセージリストを作成します。
 このサンプルコードではLivetを使用しています。
 DispatcherCollection<T>やPropertyChangedEventListenerはLivetの機能です。
 DispatcherCollectionは項目の追加削除等をDispatcherを通じて行うコレクションです。
 PropertyChangedEventListenerはModelのプロパティ変更通知をViewModelが把握するための機構です。
 ここではModelのプロパティ変更通知が来たら、ViewModelでも同じプロパティ名で変更通知が出るようにしています。(ModelのIsSamplingが変更されたときにViewModelのIsSampling変更通知が出るようにしています)
 あとは制御用のプロパティと処理実行メソッドがあるだけなので説明は省略します。

 最後にViewのコードです。
<Window x:Class="ParallelProgress.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
        xmlns:l="http://schemas.livet-mvvm.net/2011/wpf"
        xmlns:v="clr-namespace:ParallelProgress.Views"
        xmlns:vm="clr-namespace:ParallelProgress.ViewModels"
        Title="MainWindow" Height="350" Width="525">
    
    <Window.DataContext>
        <vm:MainWindowViewModel/>
    </Window.DataContext>
    
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Closed">
            <l:DataContextDisposeAction/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
    
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        
        <Button Grid.Row="0" Grid.Column="0" Content="処理開始" IsEnabled="{Binding IsEnabled}">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="Click">
                    <l:LivetCallMethodAction MethodTarget="{Binding}" MethodName="HeaveyWorkAsync"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </Button>
        <CheckBox Grid.Row="0" Grid.Column="1"
                  Content="作業状況通知時にサンプリングを行う。"
                  IsChecked="{Binding IsSampling}"/>
        
        <ListBox Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"
                 ItemsSource="{Binding LogMessageList}"/>
        
        <ProgressBar Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2"
                     Height="20"
                     Minimum="0" 
                     Value="{Binding ProgressValue}"
                     Maximum="{Binding ProgressTotal}"/>
    </Grid>
</Window>
 処理実行と作業通知を表示するためのコントロールを配置しているだけなので説明は省略します。
 「作業状況通知時にサンプリングを行う。」にチェックを入れることで、作業状況のサンプリングが行われます。
 サンプリングなしで実行した場合は以下の様になります。
サンプリングなし状況通知
 サンプリングを行っていないので全ての状況通知が実行されます。
 並列処理を行っているため作業が実行される順番は制御できません。そのため500/500(図で選択されている行)のあとに496/500が表示されています。
 綺麗に500/500で終了させるためには、並列処理が終わった後に終了通知を出す必要があります。(今回もそうしています)
 サンプリングを行った場合は以下の様になります。
サンプリングあり状況通知
 見ての通り、通知回数が激減しています。
 サンプリングを行っている場合も並列処理終了後に終了通知を出す必要があります。
 これを行わないと500/500(100%の通知)が来ないため、プログレスバーが中途半端なまま終了してしまいます。

 並列処理時に状況通知回数を減らす意味がどこまであるかわかりませんが、Rxを使えば簡単に状況通知回数を減らしてUI更新の負担をへらせますよというサンプルでした。
カレンダー
04 | 2015/05 | 06
- - - - - 1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31 - - - - - -
全記事表示リンク

全ての記事を表示する

カテゴリ
タグリスト

月別アーカイブ
08  07  06  05  04  03  02  01  12  11  10  09  08  07  06  04  03  02  01  12  11  10  09  08  07  06  05  04  03  02  01  12  11  10  09 
最新記事
リンク
最新コメント
検索フォーム
Amazon