8

ファイル I/O の進行状況を報告するために使用するプログレスバーとキャンセル ボタンがある ProgressBarWindow があります。ただし、バックグラウンドワーカーですべての作業が行われているにもかかわらず、ProgressBarWindow の UI スレッドとメイン ウィンドウの両方がハングします。プログレスバーは、メイン ウィンドウと同様にレンダリングされますが、バックグラウンド ワーカーが作業を行っている間は更新されません。次のコードは、メイン ウィンドウのコンストラクターの最後で呼び出されます。

iCountLogLinesProgressBar = new ProgressBarWindow();
iCountLogLinesProgressBar.cancelButton.Click += EventCountLogLinesProgressBarCancelButtonClicked;
iCountLogLinesProgressBar.Show();

iCountLogRecords = new BackgroundWorker();
iCountLogRecords.DoWork += EventCountLogLinesDoWork;
iCountLogRecords.ProgressChanged += EventCountLogLinesProgressChanged;
iCountLogRecords.RunWorkerCompleted += EventCountLogLinesRunWorkerCompleted;
iCountLogRecords.WorkerReportsProgress = true;
iCountLogRecords.WorkerSupportsCancellation = true;
iCountLogRecords.RunWorkerAsync(new BinaryReader(File.Open(iMainLogFilename, FileMode.Open, FileAccess.Read)));

EventCountLogLinesProgressChanged() は次のようになります。

private void EventCountLogLinesProgressChanged(object sender, ProgressChangedEventArgs e)
{
    iCountLogLinesProgressBar.Value = e.ProgressPercentage;
}

これは ProgressBarWindow の短縮版です (残りはセッターの数だけです):

public partial class ProgressBarWindow : Window
{
    public ProgressBarWindow()
    {
        InitializeComponent();
        this.progressBar.Value = this.progressBar.Minimum = 0;
        this.progressBar.Maximum = 100;
    }

    public double Value
    {
        get
        {
            return progressBar.Value;
        }
        set
        {
            this.progressBar.Value = value;
        }
    }
} 

値セッター行をdispatcher.invokeデリゲートでラップしようとしましたが、スタックオーバーフローが発生します(backgroundworkerがUI ThreadでProgressChangedを呼び出すので、dispatcher.invoke行を持つ必要はありませんよね?)。私はmsdnとgoogledをチェックしましたが、この問題を抱えている人を他に見つけることができないようです.

編集申し訳ありませんが、単純化したコードが UI スレッドをブロックしていることに気付きませんでした。バックグラウンドワーカーを使用しているにもかかわらず、まったく同じ動作が得られるため、それらが同等であると誤って想定しました。バックグラウンドワーカーを使用していたことに言及する必要がありました:P

4

2 に答える 2

15

UI スレッドをブロックしているため、ループが終了するまで UI は再レンダリングされません。

バックグラウンド処理を別のスレッドに移動し、適切なDispatcher呼び出し (またはBackgroundWorker) を使用して、UI 更新呼び出しを UI スレッドにマーシャリングします。

プログレス バーが本当に単なるタイマーである場合は、いずれかのTimerクラスを使用して更新できます。

編集:さて、コードを変更しました。私には問題ないようです。UIスレッドでUIを変更するだけなので、スレッドセーフである必要があります。バックグラウンド ワーカーは確実に定期的に進捗状況を報告していますか?

于 2010-08-02T11:03:33.767 に答える
3

Jon Skeet に耳を傾けるか、 Application.DoEvents() を呼び出して、すべての UI 更新を同じスレッドで行うことができます。

于 2010-08-02T11:04:56.573 に答える