0

I have some process heavy tasks that run in my WinForms app. The problem is, while its running, it freeze the UI (UI main thread).

I haven't worked that much with threads and delegates in C# yet, and that's why I hope someone could help me to, how to handle those process heavy tasks, without freezing the UI, so the user don't think the app is crashing while waiting?

Eg. I have a call through my FrontController, that takes time:

_controller.testExportExcel(wrapper, saveDialog.FileName);

Since it's creating an Excel file. I won't the app to be responding on the UI while its working.

Another example of a process heavy task could be this:

private void dataGridView_liste_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
  if (e.ListChangedType != ListChangedType.ItemDeleted)
  {
     foreach (DataGridViewRow r in dataGridView_liste.Rows)
     {
       DataGridViewCellStyle red = dataGridView_liste.DefaultCellStyle.Clone();
       red.BackColor = Color.LightGreen;
       if (r.Cells["News"].Value != null && (bool)r.Cells["News"].Value == true)
         r.DefaultCellStyle = red;
     }
  }
}

Where the foreach loop takes time, and freeze the UI. An async thread running the process and automatically closing when its done, could be useful I think. But how does it work??

4

3 回答 3

3

如何使用任务(如果针对 .net 4)?这被认为是 BackgroundWorker 类的替代品,因为它支持嵌套(父/子任务)、任务延续等。

例如

    private void dataGridView_liste_DataBindingComplete(object sender,
      DataGridViewBindingCompleteEventArgs e)  
    {
        Task t = Task.Factory.StartNew(() =>
        {
            // do your processing here - remember to call Invoke or BeginInvoke if
            // calling a UI object.
        });
        t.ContinueWith((Success) =>
        {
            // callback when task is complete.
        }, TaskContinuationOptions.NotOnFaulted);
        t.ContinueWith((Fail) =>
        {
            //log the exception i.e.: Fail.Exception.InnerException);
        }, TaskContinuationOptions.OnlyOnFaulted);

    }
于 2012-09-27T15:15:41.503 回答
2

我在这里回答了一个非常相似的问题

归结为使用 BackgroundWorker。

msdn提供了一个示例:

using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;

namespace SL_BackgroundWorker_CS
{
    public partial class Page : UserControl
    {
        private BackgroundWorker bw = new BackgroundWorker();

        public Page()
        {
            InitializeComponent();

            bw.WorkerReportsProgress = true;
            bw.WorkerSupportsCancellation = true;
            bw.DoWork += new DoWorkEventHandler(bw_DoWork);
            bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
            bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
        }
        private void buttonStart_Click(object sender, RoutedEventArgs e)
        {
            if (bw.IsBusy != true)
            {
                bw.RunWorkerAsync();
            }
        }
        private void buttonCancel_Click(object sender, RoutedEventArgs e)
        {
            if (bw.WorkerSupportsCancellation == true)
            {
                bw.CancelAsync();
            }
        }
        private void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;

            for (int i = 1; (i <= 10); i++)
            {
                if ((worker.CancellationPending == true))
                {
                    e.Cancel = true;
                    break;
                }
                else
                {
                    // Perform a time consuming operation and report progress.
                    System.Threading.Thread.Sleep(500);
                    worker.ReportProgress((i * 10));
                }
            }
        }
        private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if ((e.Cancelled == true))
            {
                this.tbProgress.Text = "Canceled!";
            }

            else if (!(e.Error == null))
            {
                this.tbProgress.Text = ("Error: " + e.Error.Message);
            }

            else
            {
                this.tbProgress.Text = "Done!";
            }
        }
        private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            this.tbProgress.Text = (e.ProgressPercentage.ToString() + "%");
        }
    }
}

在 DoWork 事件处理程序中运行的所有内容都是异步的。
在 ProgessChanged/RunWorkCompleted 的事件处理程序中运行的所有内容都在 UI 线程上。

于 2012-09-27T15:10:50.980 回答
0

对于您的第一个示例,调用_controller.testExportExcel()、 aBackgroundWorkerTask Parallel Library调用(即Task.Factory.StartNew(...))将适合满足您保持 UI 响应的要求。周围有很多例子,包括这里的其他答案。

对于第二个示例,您会发现不能将其放在后台线程上,因为它似乎是操纵 UI 的代码。具体来说,如果你BackgroundWorkerDoWork事件处理程序的实现,或者你传递给的委托Task.Factory.StartNew(),或者一个普通的旧线程的方法接触到 UI,你很可能(/确定?)得到一个异常,说明“跨线程操作无效”。

其原因已包含在此问题中。但我更惊讶的是,这速度慢到你想让它异步。可能有一些简单的方法可以使此代码更具响应性 -Control.SuspendLayout()并且.ResumeLayout()浮现在脑海中。

于 2012-09-27T15:32:14.813 回答