保持响应式用户界面

时间:2012-07-13 08:32:16

标签: c# .net multithreading gridview long-running-processes

我有一个winform,它有几个组合框和一个gridview。

最初我使用行和列创建gridview,并且没有填充数据。

enter image description here

填充数据到网格是一个长时间运行的任务,它将遍历所有行并读取列标题,并根据它将不同的颜色和数据应用于每个单元格。

我想要实现的是在表单加载事件中加载网格,并在表单加载后开始将数据填充到网格,以便用户可以看到发生了什么。同样的事情适用于组合框值的更改,因为我将根据组合值加载数据。

我试过的是这样的......

在表单加载中我正在调用方法

 private void LoadForm()
        {                        
            DataBind(); // this will load the initial grid without cell data            

            this.BeginInvoke((MethodInvoker)this.LongRunningProcess1);

            this.BeginInvoke((MethodInvoker)this.LongRunningProcess2);

         }

但仍然需要很长时间,而且我没有响应式用户界面。

我也试过这样的事但没有运气......

            ThreadStart ts = LongRunningProcess1;
            Thread t1 = new Thread(ts);
            t1.Start();

同样使用后台工作程序来完成长时间运行操作会导致 “跨线程操作” 问题。

 private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
                LongRunningProcess1();
                LongRunningProcess2();           
        }

任何使这项工作有所帮助的人都很感激..

谢谢

更新

我找到了一个非常酷的解决方案Updating Your Form from Another Thread without Creating Delegates for Every Type of Update

感谢您的回答!!!

4 个答案:

答案 0 :(得分:1)

要避免后台工作程序的完成事件中的CrossThreadException,请将回调方法包装为:

public void WorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (this.InvokeRequired)
    {
        this.Invoke(new RunWorkerCompletedEventHandler(WorkerCompleted), new {sender, e});
    }
    else 
    {
        // Business logic goes here
    }
}

通常,您将在else块中填充加载到GridView中的数据。另一方面,如果您希望从长时间运行的后台任务中逐步填充GridView,则可以使用来自后台工作程序的回调的类似技术来实现此目的:

public void Worker_DoWork(object sender, DoWorkEventArgs e)
{
   foreach (var someParameter in parameterList) // Long-running loop 
   {
     var data = LoadData(someParameter); // Load data for row X
     this.Invoke(new Action<object>(UpdateRow),new[]{data});  // Update on UI-thread
   }
}


public void UpdateRow(object data)
{
   // Code to populate DataGrid row X with data from argument
}

请注意,如果要异步进行UI更新,可以调用BeginInvoke而不是Invoke。在这种情况下,这通常没有区别。

答案 1 :(得分:0)

背景工作者流程是正确的方法,你只需要消除所有&#34;交叉线程操作&#34;通过确保修改表单元素的所有调用都使用Control.Invoke方法包装器来实现异常。

答案 2 :(得分:0)

如果要使用backgroundworker / multithreading,可以使用委托更新表单(它们在ui线程上运行)。请参阅此处的示例:How to update the GUI from another thread in C#?

答案 3 :(得分:0)

使用BackgroundWorker和RunWorkerAsync的简单示例。希望这会有所帮助。

public partial class Form2 : Form
{

    BackgroundWorker worker = new BackgroundWorker();

    public Form2()
    {

        worker.WorkerReportsProgress = true;
        worker.WorkerSupportsCancellation = true;

        worker.DoWork += new DoWorkEventHandler(worker_DoWork);
        worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
        worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);

        InitializeComponent();
    }

    void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        int totalSteps = 5;
        for (int i = 1; i <= totalSteps; i++)
        {
            Thread.Sleep(1000);
            worker.ReportProgress(i);
        }
    }

    void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        progressBar1.Value = e.ProgressPercentage;
    }

    void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        resultText.Text = "Worker complete";
        btnDoWork.Enabled = true;
        progressBar1.Visible = false;
    }

    private void btnDoWork_Click(object sender, EventArgs e)
    {
        progressBar1.Visible = true;
        worker.RunWorkerAsync();
    }

}