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 线程。

将工作分为两部分:

  • 缓慢的部分,正在检索文件,应该在外部完成Dispatcher.Invoke
  • UI 更新,必须在事件处理程序Dispatcher.Invoke中或(更好)中完成。RunWorkerCompleted

后台工作组件是完全制作的,因此您无需使用调度程序手动调度 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

使用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 回答
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 回答