none
DataGrid 上の ProgressBar の進捗更新 RRS feed

  • 質問

  • いつもお世話になっております。
    基本的すぎる質問かもしれませんが、調べても解決策がわからず嵌ってしまったため、
    ご質問させていただきます。
     
    DataGrid に DataGridTemplateColumn で ProgressBar を実装しています。
    ProgressBar の進捗更新には WebClient の DownloadProgressChanged イベントで更新を行っているのですが、
    DownloadProgressChanged イベントでは、デバッガでブレークして再開した際に稀に ProgressBar の進捗が更新されることがあるのですが、
    ほぼ DownloadProgressChanged イベントでは進捗が更新されずに DownloadDataCompleted イベントで値を設定すると反映されます。
    スレッドを跨いでいるわけでもありませんし、WebClient を使わずにデータソースを更新すると ProgressBar の値に反映されるので、
    データソースそのものにも問題は無いように思えます。
    何が問題となっているのでしょうか?
     
    <DataGrid Name="SampleGrid">
        <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding Path=Key}" IsReadOnly="True" Width="200" />
            <DataGridTemplateColumn IsReadOnly="True">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <ProgressBar Minimum="0" Maximum="100" Width="200" Value="{Binding Path=Value.Progress, Mode=OneWay}" />
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>
    
    
     
     
    public class Sample : INotifyPropertyChanged
    {
        private double ProgressValue;
        public double Progress
        {
            get { return this.ProgressValue; }
            set
            {
                if( value != this.ProgressValue ) {
                    this.ProgressValue = value;
                    NotifyPropertyChanged( "Progress" );
                }
            }
        }
    
        #region INotifyPropertyChanged メンバー
    
        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged( string PropertyName )
        {
            if( PropertyChanged != null )
                PropertyChanged( this, new PropertyChangedEventArgs( PropertyName ) );
        }
    
        #endregion
    }
    
    public partial class MainWindow : Window
    {
        // 実際は異なるstaticクラスで定義されている
        public static Dictionary<string, Sample> dic = new Dictionary<string, Sample>();
    
        public MainWindow()
        {
            InitializeComponent();
    
            this.Loaded += new RoutedEventHandler( MainWindow_Loaded );
        }
    
        private void MainWindow_Loaded( object sender, RoutedEventArgs e )
        {
            dic.Add( "sample", new Sample() );
    
            this.SampleGrid.ItemsSource = dic;
    
            var wc = new WebClient();
            wc.DownloadProgressChanged += new DownloadProgressChangedEventHandler( DownloadProgressChanged );
            wc.DownloadDataCompleted += new DownloadDataCompletedEventHandler( DownloadDataCompleted );
            wc.DownloadDataAsync( new Uri( "..." ) );
        }
    
        private void DownloadProgressChanged( object sender, DownloadProgressChangedEventArgs e )
        {
            dic["sample"].Progress = ((double)e.BytesReceived / (double)e.TotalBytesToReceive) * 100.0;
        }
    
        private void DownloadDataCompleted( object sender, DownloadDataCompletedEventArgs e )
        {
            dic["sample"].Progress = 100.0;
        }
    }
    
    


    よろしくお願いいたします。
     
     

    環境
    Windows7 (64bit)
    .NetFramework 4.0

    • 編集済み Nymphaea 2011年11月24日 21:54
    2011年11月24日 13:47

回答

  • ちゃんとProgressChangedで値が取れる環境で試してみました。

    質問されている内容のコードをコピペする形で作成したサンプルプログラムですがプログレスバーは進捗が表示されてました。環境はいっしょっぽいですね・・・。

    .NET Framework 4
    Windows 7(64bit)


    かずき Blog:http://d.hatena.ne.jp/okazuki/ VS 2010のデザイナでBlendのBehaviorをサポートするツール公開してます。 http://vsbehaviorsupport.codeplex.com/
    • 回答としてマーク Nymphaea 2011年12月5日 12:44
    2011年12月2日 2:53

すべての返信

  • そもそも、TotalBytesToReceiveには期待する値が入ってるでしょうか?
    詳しくはわからないのですが、私が試したProxyが間に噛んでる環境だと-1になってました。

    そのため、計算結果がマイナスの数字になってプログレスバーに進捗が表示されてないといった感じになりました。そちらではどうでしょう。


    かずき Blog:http://d.hatena.ne.jp/okazuki/ VS 2010のデザイナでBlendのBehaviorをサポートするツール公開してます。 http://vsbehaviorsupport.codeplex.com/
    2011年11月30日 3:16
  • かずき様 ご返信ありがとうございます

     

    > そもそも、TotalBytesToReceiveには期待する値が入ってるでしょうか?

    こちら、TotalBytesToReceive には期待する値が入っていることは確認済みです

     

    また、以下の方法で確認を行いました
    1.VisualTree を辿って ProgressBar の Value プロパティに直接、値を設定した場合には進捗が更新されます
    2.Value プロパティのバインディングソースとなる Sample.Progress の型を double 型から int 型に変更した場合には進捗が更新されます
    Sample.Progress で使用する値は桁数が決まっているため、桁上げをすることで期待する動作になります
    (int 型の e.ProgressPercentage を直接指定する方法でも進捗は更新されます)

    1の方法は VisualTree が非常に深いため、パフォーマンス的にもこの方法は無いかなと思います

    2の方法は System.Windows.Controls.ProgressBar の Value プロパティの型はそもそも double 型なので、
    int がよくて double がダメな理由がよくわかりません・・・
    double 型のデータは TextBlock での表示では正しく表示されていますので、データソースの問題ではなさそうです

    また、2の方法をもとに ValueConverter を利用して double 型にバインドしつつ、コンバーターで int 型に変換しても進捗が更新されています

     

    やりたいことは int 型で事足りるのですが、なんだかすっきりしませんね・・・
    (データソースは double 型で変更はあまりしたくないので、生データをそのまま使えれば一番良いのですが)

    2011年11月30日 11:21
  • ちゃんとProgressChangedで値が取れる環境で試してみました。

    質問されている内容のコードをコピペする形で作成したサンプルプログラムですがプログレスバーは進捗が表示されてました。環境はいっしょっぽいですね・・・。

    .NET Framework 4
    Windows 7(64bit)


    かずき Blog:http://d.hatena.ne.jp/okazuki/ VS 2010のデザイナでBlendのBehaviorをサポートするツール公開してます。 http://vsbehaviorsupport.codeplex.com/
    • 回答としてマーク Nymphaea 2011年12月5日 12:44
    2011年12月2日 2:53
  • かずき様 再度ご確認いただきありがとうございました

    こちらでもかずき様の検証結果から、別な環境を用意して再度検証してみましたところ確かに同じコードで進捗が表示されました

     

    環境の違いとして、ネットワークの速度が開発環境がたまたま 200MB/s ~ 400MB/s の環境だったため、
    更新速度が速すぎて進捗更新が追随しなかったのではないかと思います・・・
    (プログレスバーの動きをみるために 1.8GB のファイルをダウンロードしても 5~6 秒程度なので)

    通常のLAN環境でテストしたところ、すべての環境で普通に進捗が更新されることを確認しました

    また、開発環境でも負荷を与えてビジー状態でテストすると更新されることを確認しています

     

    実環境では通常のLAN環境なのですが、更新されない可能性があることが分かったため桁上げとコンバーターを使用して実装することにしました

    かずき様 検証いただきありがとうございました

     

     

    ※どなたか、本当に進捗更新が追従していないだけなのか理由をご存知の方いらっしゃいましたらコメントを頂けると助かります

    2011年12月5日 12:43