使用BackgroundWorker更新C#图表

4

我目前正在尝试使用后台工作程序将我的表单上的图表更新到:

bwCharter.RunWorkerAsync(chart1);

这段代码运行:

private void bcCharter_DoWork(object sender, DoWorkEventArgs e)
{
     System.Windows.Forms.DataVisualization.Charting.Chart chart = null;

     // Convert e.Argument to chart
     //..
     // Converted..

     chart.Series.Clear();
     e.Result=chart;

     setChart(c.chart);
}
private void setChart(System.Windows.Forms.DataVisualization.Charting.Chart arg)
{
     if (chart1.InvokeRequired)
     {
         chart1.Invoke(new MethodInvoker(delegate { setChart(arg); }));
         return;
     }

     chart1 = arg;
}

然而,在清理系列的时候,会引发异常。

基本上,在清理系列后,我想要做更多的处理,这会完全减慢GUI的速度 - 所以希望在另一个线程中进行。

我认为通过将它作为参数传递,应该是安全的,但显然不是!

有趣的是,图表在一个选项卡页上。如果选项卡页在后台运行,我可以一遍又一遍地运行此操作,但如果我运行此操作,查看图表,然后再隐藏它,并重新运行,则会抛出异常。显然,如果图表在前景中,则会抛出异常。

有人能建议我如何进行不同的操作吗?

谢谢!


编辑: 我知道这可以在窗体线程中完成,因为当我再次分配它时是这样的。然而,使用后台工作程序的整个目的是避免使整个程序停顿。正如我所说,有比这个命令更多的处理。

我认为通过将其作为参数传递,可以无障碍地在那个线程中访问它,但是否有可能这个传递的图表仍然以某种方式指向原始图表?如果是这样,该如何解决?

我希望尽可能少地阻止GUI线程 - 因此似乎没有仅调用每个命令的意义。

4个回答

4
如果您想要在重新显示之前先清除它,为什么不在调用BackgroundWorker之前调用chart.Series.Clear();?这样可以在主UI线程上清除它,然后在执行一些异步工作后再从UI线程设置图表。同时,在使用BackgroundWorker时,建议使用内置的ReportProgressWorkerCompleted事件来避免手动跨线程调用。这也是使用BackgroundWorker的原因之一,可以获得这种类型的功能而无需额外付出。因此,应该在WorkerCompleted中设置图表以简化代码(即使在这种情况下并非问题的根源)。

问题在于还有很多其他的处理工作要做。我想要一个后台工作者的原因是为了保持GUI的响应性。所以,如果我调用后台工作者,只是告诉它在GUI线程中完成所有工作,那么就没有优势了。我希望的是复制图表,让后台工作者处理它,然后将其传回并将新图表设置在旧图表的位置。这不可能吗? - Mark
1
你不能让BackgroundWorker处理所有的工作,然后在WorkerCompleted事件中清除旧图表并插入新图表吗?如果将e.Result设置为新图表,则也可以从WorkerCompleted方法中使用它。 - Øyvind Bråthen
同样的事情发生了。问题不在于我尝试分配图表(如上所示,这是在GUI线程中调用的)。问题在于操作作为参数传递的图表。即使图表已经被传递,它仍然只能看到原始对象吗?如果它被作为引用传递,那么这肯定是这种情况,对吗? - Mark
只有引用被传递,因此对传递的引用所做的任何操作都会影响属于GUI线程的原始对象。 - Øyvind Bråthen
该值被复制到方法中,更改不会在外部反映。但是,使用ref关键字发送它将使方法内部的任何更改也反映在原始对象上。 - Øyvind Bråthen
显示剩余5条评论

1
在bcCharter_DoWork中检查是否需要Invoke Required,如果是,则将Clear方法调用也放入委托中。
 if (InvokeRequired)
 {          

     Invoke(new MethodInvoker(delegate 
     { 
       chart.Series.Clear();
       e.Result=chart;               
     }));

     return;
 }

1
我同意之前帖子中的分析:您要求一个线程访问另一个线程的资源。
就像您使用了BackgroundWorker一样,我建议您使用Dispatcher:
private void bcCharter_DoWork(object sender, DoWorkEventArgs e)
{
    Chart chart = null;

    Dispatcher.Invoke(DispatcherPriority.Normal,
        new Action(() =>
            {
                chart.Series.Clear();
            }));
}

0
你遇到的问题是无法在创建它们的线程之外的线程上访问UI元素。但是,在你的情况下,你可以在调用后台工作程序之前简单地清除图表。
你可以使用Control.Invoke手动将UI元素访问封送到另一个线程的正确线程。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接