Background worker does not work properly in WPF

℡╲_俬逩灬. 提交于 2020-02-06 07:23:22

问题


i want to show Temporary files in a datagrid , hence it is a long term process i use background worker in my C# .net WPF application .

my Code is

 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");
          }

     }

All the code is running but it hangs when datagrid is loading means my UI does not response when program is running .

What modification is needed to run background worker smoothly in the above condition ?

Beside it , if i want to add a ProgressBar which progressed along with this application then what i have to do ?

Thank You


回答1:


By using this.Dispatcher.Invoke, you are effectively marshalling the work back to the UI thread. This makes no sense: you are blocking the UI thread while this action is being performed.

Split the work in two parts:

  • the slow part, which is retrieving the files and should be done outside the Dispatcher.Invoke
  • the UI update, which must be done in Dispatcher.Invoke, or (better) in the RunWorkerCompleted event handler.

The background worker component is made exactly so that you don't need to dispatch UI work manually using the dispatcher. For example, you could store the files in a field which you fill in the DoWork method, and use to fill the datagrid in the RunWorkerCompleted event:

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);           
         }
    }
}

Note: if you are using C# 5, you now have an even easier way, using the async/await feature. All you need is something like this:

    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;
        }
    }

All the rest of the cruft is taken care of by the compiler.




回答2:


Try to take this action off the Dispatcher:

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

It should only do small and quick operations that involve UI access or modification. See this link: http://msdn.microsoft.com/en-us/magazine/cc163328.aspx

Your heavy work can be done by the BackgroundWorker and use the Dispatcher to update the dataGrid.Items collection.

Try to use the Dispatcher using:

Dispatcher.BeginInvoke()



回答3:


Use DirectoryInfo.EnumerateFiles.

This line:

this.Dispatcher.Invoke

executes code synchronously at main thread, so there are no benefits from using of BackgroudWorker such a way, because DirectoryInfo.GetFiles returns only when all files in directory are enumerated.

On the other hand, DirectoryInfo.EnumerateFiles is lazy. You can write something like this:

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)));
    }
}



回答4:


You should be updating the UI in either the RunWorkerComplete or ProgressChanged event handlers.

Try something like this:

    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;
    }

Also, there is no need for the try/catch in dowork, exceptions are automatically caught and reported as errors in the runworkercomplete event.



来源:https://stackoverflow.com/questions/12706651/background-worker-does-not-work-properly-in-wpf

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!