在BackgroundWorker中填充WPF图表

时间:2011-03-29 20:38:02

标签: c# wpf backgroundworker wpftoolkit charts

我的WPF程序将多个CSV文件中的数据加载到折线图中(每个文件都放入其自己的LineSeries中)。这需要一些时间(使GUI无法使用),所以我想在一个单独的线程中执行此操作并显示加载消息(来自Extended WPF Toolkit的BusyIndi​​cator)。

不幸的是,当我尝试在BackgroundWorker中创建LineSeries时,我得到一个例外:“调用线程必须是STA,因为许多UI组件都需要这个。”我正在尝试复制GUI的图表,填充副本,然后在完成后将其复制到GUI的图表中。所以这不应该尝试从不同的线程访问控件。

    /// <summary>
    /// Loads data from the collections into the chart
    /// </summary>
    private void populateChart()
    {
        // Begin working: Pass chart and data to the worker (wrapped in a class)
        this.chartWorker.RunWorkerAsync(new ChartWorkerArgs()
        {
            chart = chart,
            data = model.getAllCollections()
        });
    }

    /// <summary>
    /// Populates a provided Chart with provided data.
    /// </summary>
    private void chartWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        // ...

        // Iterate through each one
        foreach (XYCollection collection in data)
        {
            // Create a new LineSeries and configure it
            LineSeries ls = new LineSeries();  // <-----------ERROR
            ls.ItemsSource = collection;
            ls.IndependentValueBinding = new Binding("X");
            ls.DependentValueBinding = new Binding("Y");
            ls.Title = collection.Name;
            chart.Series.Add(ls);
        }

        // Send the pouplated chart back
        e.Result = chart;
    }

    /// <summary>
    /// After chart has been populated (or cancelled), update chart.
    /// </summary>
    private void chartWorker_RunWorkerCompleted(
        object sender, RunWorkerCompletedEventArgs e)
    {
        // ... 
        // Set the GUI's chart as the newly populated chart
        this.chart = (Chart)e.Result;
        // ... 
    }

/// <summary> /// Loads data from the collections into the chart /// </summary> private void populateChart() { // Begin working: Pass chart and data to the worker (wrapped in a class) this.chartWorker.RunWorkerAsync(new ChartWorkerArgs() { chart = chart, data = model.getAllCollections() }); } /// <summary> /// Populates a provided Chart with provided data. /// </summary> private void chartWorker_DoWork(object sender, DoWorkEventArgs e) { // ... // Iterate through each one foreach (XYCollection collection in data) { // Create a new LineSeries and configure it LineSeries ls = new LineSeries(); // <-----------ERROR ls.ItemsSource = collection; ls.IndependentValueBinding = new Binding("X"); ls.DependentValueBinding = new Binding("Y"); ls.Title = collection.Name; chart.Series.Add(ls); } // Send the pouplated chart back e.Result = chart; } /// <summary> /// After chart has been populated (or cancelled), update chart. /// </summary> private void chartWorker_RunWorkerCompleted( object sender, RunWorkerCompletedEventArgs e) { // ... // Set the GUI's chart as the newly populated chart this.chart = (Chart)e.Result; // ... }

从我在其他地方读到的内容来看,它不可能成为一个BackgroundWorker STA,那么还有其他方法可以在不挂起GUI的情况下使用数据加载图表吗?

由于

2 个答案:

答案 0 :(得分:2)

您不应该从后台工作者访问UI元素。 WPF UI(几乎)是单线程的。这意味着您必须在后台线程中加载数据,并更新UI调用UI线程中所需的函数(使用Dispatcher.Invoke)。

所以基本上你必须在你的后台线程中做所有必要的(和冗长的)数据准备(无论你是使用BackgroundWorker还是其他任何一个多线程设备都没关系,但是创建UI元素的维护和视觉状态的维护必须Invoke进入主线程。

Here你可以找到一个简单的例子。

答案 1 :(得分:0)

您可以使用普通线程。然后你有控制权(所有权)使其成为STA。

Bgw添加了一些方便的功能来与GUI交互,没有什么是你自己不能用几行代码编写的。你甚至没有使用UpdateProgress。