0

データグリッドに一時ファイルを表示したいので、C# .net WPF アプリケーションでバックグラウンド ワーカーを使用する長期的なプロセスです。

私のコードは

 private System.ComponentModel.BackgroundWorker _background = new System.ComponentModel.BackgroundWorker();

   private void button1_Click(object sender, RoutedEventArgs e)
        {
          _background.RunWorkerAsync();
        }

    public MainWindow()
       {
           InitializeComponent();
           this._background.DoWork += new DoWorkEventHandler(_background_DoWork);
           this._background.RunWorkerCompleted += new       
           RunWorkerCompletedEventHandler(_background_RunWorkerCompleted);
           this._background.WorkerReportsProgress = true;
           _background.WorkerSupportsCancellation = true;

       }


void _background_DoWork(object sender, DoWorkEventArgs e)

{

this.Dispatcher.Invoke((Action)(() =>
    {
        try
        {
            FileInfo[] files = new   
            DirectoryInfo(System.IO.Path.GetTempPath()).GetFiles();

            foreach (FileInfo fi in files)
            {
                if (fi != null)              
                 {                 
                    dataGrid1.Items.Add(fi);           

                }
            }           
        }
        catch { }
    }));
}

void _background_RunWorkerCompleted(object sen, RunWorkerCompletedEventArgs e)
      {

          if (e.Cancelled)
          {
             MessageBox.Show("Cancelled");
          }
          else if (e.Error != null)
          {
               MessageBox.Show("Exception Thrown");
          }

     }

すべてのコードが実行されていますが、データグリッドの読み込み中にハングするということは、プログラムの実行中に UI が応答しないことを意味します。

上記の状態でバックグラウンド ワーカーをスムーズに実行するには、どのような変更が必要ですか?

それに加えて、このアプリケーションと共に進行する ProgressBar を追加したい場合は、どうすればよいですか?

ありがとうございました

4

4 に答える 4

3

を使用this.Dispatcher.Invokeすることで、効果的に作業を UI スレッドにマーシャリングします。これは意味がありません。このアクションが実行されている間、UI スレッドをブロックしています。

作業を 2 つの部分に分割します。

  • ファイルを取得する遅い部分であり、外部で実行する必要がありますDispatcher.Invoke
  • Dispatcher.Invoke、または (より良い)RunWorkerCompletedイベント ハンドラーで実行する必要がある UI の更新。

バックグラウンド ワーカー コンポーネントは、ディスパッチャーを使用して手動で UI 作業をディスパッチする必要がないように正確に作成されています。たとえば、メソッドに入力するフィールドにファイルを保存し、イベントDoWorkでデータグリッドを入力するために使用できます。RunWorkerCompleted

FileInfo[] files;

void _background_DoWork(object sender, DoWorkEventArgs e)
{
    files = new DirectoryInfo(System.IO.Path.GetTempPath()).GetFiles();
}

void _background_RunWorkerCompleted(object sen, RunWorkerCompletedEventArgs e)
{
    if (e.Cancelled)
    {
        MessageBox.Show("Cancelled");
    }
    else if (e.Error != null)
    {
        MessageBox.Show("Exception Thrown");
    }
    else 
    { 
         foreach (FileInfo fi in files)
         {
              dataGrid1.Items.Add(fi);           
         }
    }
}

注: C# 5 を使用している場合は、このasync/await機能を使用するとさらに簡単な方法が得られます。必要なのは次のようなものだけです。

    private async void button1_Click(object sender, EventArgs e)
    {
        button1.Enabled = false;
        try
        {
            var files = await Task.Run(() => 
                new DirectoryInfo(System.IO.Path.GetTempPath()).GetFiles()
            );
            foreach (var f in files)
                this.dataGrid1.Items.Add(f.Name);
        }
        catch (Exception e)
        {
            MessageBox.Show("Exception thrown!"); // do proper error handling here...
        }
        finally
        {
            button1.Enabled = true;
        }
    }

残りの面倒なことはすべて、コンパイラーによって処理されます。

于 2012-10-03T10:52:52.200 に答える
0

RunWorkerCompleteまたはProgressChangedイベントハンドラーのいずれかでUIを更新する必要があります。

次のようなものを試してください。

    public Program()
    {
        w = new BackgroundWorker();
        w.DoWork += new DoWorkEventHandler(w_DoWork);
        w.ProgressChanged += new ProgressChangedEventHandler(w_ProgressChanged);
        w.RunWorkerCompleted += new RunWorkerCompletedEventHandler(w_RunWorkerCompleted);
        w.WorkerReportsProgress = true;
        w.WorkerSupportsCancellation = true;

        w.RunWorkerAsync();
    }

    void w_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        FileInfo[] files = e.Result as FileInfo[];
        foreach (FileInfo fi in files)
        {
            //dataGrid1.Items.Add(fi);  
        }
    }

    void w_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        FileInfo fi = e.UserState as FileInfo;
        //dataGrid1.Items.Add(fi);  
    }

    void w_DoWork(object sender, DoWorkEventArgs e)
    {
        var w = sender as BackgroundWorker;

        FileInfo[] files = new DirectoryInfo(
            Path.GetTempPath()).GetFiles();

        // Using ProgressChanged
        foreach (FileInfo fi in files)
        {
            w.ReportProgress(0, fi);
        }

        // Using RunWorkerCompleted
        e.Result = files;
    }

また、doworkでtry / catchを実行する必要はありません。例外は自動的にキャッチされ、runworkercompleteイベントでエラーとして報告されます。

于 2012-10-03T14:28:28.823 に答える
0

DirectoryInfo.EnumerateFilesを使用します。

この行:

this.Dispatcher.Invoke

メインスレッドでコードを同期的に実行するため、ディレクトリ内のすべてのファイルが列挙された場合にのみ戻るBackgroudWorkerため、このような方法を使用してもメリットはありません。DirectoryInfo.GetFiles

一方、DirectoryInfo.EnumerateFiles怠け者です。次のように書くことができます。

void _background_DoWork(object sender, DoWorkEventArgs e)
{
    var info = new DirectoryInfo(System.IO.Path.GetTempPath());

    // now enumeration happens in background
    foreach (var fi in info.EnumerateFiles())
    {
        // main thread in used only when there is next enumeration result available
        Dispatcher.Invoke((Action)(() => dataGrid1.Items.Add(fi)));
    }
}
于 2012-10-03T10:36:46.913 に答える
0

このアクションを Dispatcher から外してみてください。

FileInfo[] files = new DirectoryInfo(System.IO.Path.GetTempPath()).GetFiles();

UI へのアクセスや変更を伴う小規模で迅速な操作のみを実行する必要があります。このリンクを参照してください: http://msdn.microsoft.com/en-us/magazine/cc163328.aspx

重い作業は BackgroundWorker によって実行でき、Dispatcher を使用して dataGrid.Items コレクションを更新できます。

以下を使用して Dispatcher を使用してみてください。

Dispatcher.BeginInvoke()
于 2012-10-03T10:41:13.563 に答える