简体   繁体   English

后台工作者在WPF中无法正常工作

[英]Background worker does not work properly in WPF

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 . 我想在datagrid中显示临时文件,因此这是一个长期的过程,我在C#.net WPF应用程序中使用后台工作程序。

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 . 所有代码都在运行,但是在加载datagrid时挂起,这意味着我的UI在程序运行时没有响应。

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 ? 在它旁边,如果我想添加一个与此应用程序一起进度的ProgressBar,那我该怎么办?

Thank You 谢谢

By using this.Dispatcher.Invoke , you are effectively marshalling the work back to the UI thread. 通过使用this.Dispatcher.Invoke ,您可以有效地将工作编组回UI线程。 This makes no sense: you are blocking the UI thread while this action is being performed. 这没有任何意义:执行此操作时,您正在阻止UI线程。

Split the work in two parts: 将工作分为两部分:

  • the slow part, which is retrieving the files and should be done outside the Dispatcher.Invoke 较慢的部分,正在检索文件,应在Dispatcher.Invoke外部完成
  • the UI update, which must be done in Dispatcher.Invoke , or (better) in the RunWorkerCompleted event handler. UI更新,必须在Dispatcher.Invoke完成,或者(最好在RunWorkerCompleted事件处理程序中)完成。

The background worker component is made exactly so that you don't need to dispatch UI work manually using the dispatcher. 后台工作程序组件是完全制成的,因此您不需要使用调度程序手动调度UI工作。 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: 例如,您可以将文件存储在填写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);           
         }
    }
}

Note: if you are using C# 5, you now have an even easier way, using the async/await feature. 注意:如果您使用的是C#5,则现在可以使用async/await功能,甚至更简单。 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. 其余所有工作由编译器处理。

Use DirectoryInfo.EnumerateFiles . 使用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. 在主线程上同步执行代码,因此使用BackgroudWorker不会带来任何好处,因为DirectoryInfo.GetFiles仅在枚举目录中的所有文件时才返回。

On the other hand, DirectoryInfo.EnumerateFiles is lazy. 另一方面, DirectoryInfo.EnumerateFiles是惰性的。 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)));
    }
}

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. 它只应执行涉及UI访问或修改的小型快速操作。 See this link: http://msdn.microsoft.com/en-us/magazine/cc163328.aspx 请参阅此链接: 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. 繁重的工作可以由BackgroundWorker完成,并使用Dispatcher更新dataGrid.Items集合。

Try to use the Dispatcher using: 尝试通过以下方式使用分派器:

Dispatcher.BeginInvoke()

You should be updating the UI in either the RunWorkerComplete or ProgressChanged event handlers. 您应该在RunWorkerComplete或ProgressChanged事件处理程序中更新UI。

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. 此外,无需在dowork中进行try / catch,在runworkercomplete事件中,异常会自动捕获并报告为错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM