2012-07-13 58 views
0

我有一个winform有几个组合框和一个gridview。保持响应的UI

最初我正在创建带有行和列的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); 

     } 

但仍须采取很长一段时间,我没有响应UI。

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

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

而且使用后台工作完成长时间运行的操作导致“跨线程操作”问题。

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

任何帮助,使这个工作是欣赏..

感谢

UPDATE

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

感谢您的答案! !

+0

我建议您使用BackgroundWorker,以便在UI线程之外执行长时间运行的操作。 http://www.switchonthecode.com/tutorials/csharp-tutorial-using-the-backgroundworker-class – Phil 2012-07-13 08:34:48

+0

首先确定究竟需要很长时间,直到“这个循环在这里,特别是这个调用内部的水平循环”。 – Jon 2012-07-13 08:36:14

+0

顺便说一句,调用'this.BeginInvoke'不会帮助你完成任何长时间运行的任务 - 它只是在UI事件循环中调度你的任务并返回。这意味着该任务将由GUI线程处理,仍冻结您的应用程序。 – undefined 2012-07-13 08:43:09

回答

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 
    } 
} 

通常,您将填充加载到GridView控件在else块中的数据。

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 
} 

注意,您可以调用BeginInvoke代替:如果您在另一方面要从长期运行的后台任务逐步填充GridView控件,您可以使用回调从背景工人用类似的技术实现这个如果要异步执行UI更新,请调用。这通常在这种情况下没有什么不同。

+0

谢谢尼尔佐尔。因此,如果我使用后台工作,我想我必须在DoWork事件中放置两个长时间运行的方法?并在我的LoadForm()中调用RunWorkerAsync ...所以在你的答案中提到的其他部分中,我该如此。如果我错了,请纠正我! – 2012-07-13 08:49:52

+0

你是对的。 RunWorkerCompleted事件和方法是可选的。加载数据后,您通常会将代码放入该块中以填充网格。我已经更新了答案,并提供了关于如何逐步更新网格的提示 – Nilzor 2012-07-13 13:28:18

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(); 
    } 

}