簡體   English   中英

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

[英]Background worker does not work properly in WPF

我想在datagrid中顯示臨時文件,因此這是一個長期的過程,我在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");
          }

     }

所有代碼都在運行,但是在加載datagrid時掛起,這意味着我的UI在程序運行時沒有響應。

在上述條件下,需要什么修改才能使后台工作者順利運行?

在它旁邊,如果我想添加一個與此應用程序一起進度的ProgressBar,那我該怎么辦?

謝謝

通過使用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;
        }
    }

其余所有工作由編譯器處理。

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

嘗試從分派器中采取以下措施:

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

它只應執行涉及UI訪問或修改的小型快速操作。 請參閱此鏈接: http : //msdn.microsoft.com/en-us/magazine/cc163328.aspx

繁重的工作可以由BackgroundWorker完成,並使用Dispatcher更新dataGrid.Items集合。

嘗試通過以下方式使用分派器:

Dispatcher.BeginInvoke()

您應該在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事件中,異常會自動捕獲並報告為錯誤。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM