在BackgroundWorker中填充WPF图表
我的 WPF 程序将多个 CSV 文件中的数据加载到折线图中(每个文件加载到其自己的 LineSeries 中)。这需要一些时间(导致 GUI 不可用),因此我想在单独的线程中执行此操作并显示加载消息(扩展 WPF 工具包中的 BusyIndicator)。
不幸的是,当我尝试在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;
// ...
}
从我在其他地方读到的内容来看,不可能制作一个BackgroundWorker STA,那么有没有其他方法可以在不挂起GUI的情况下加载图表数据?
谢谢
My WPF program loads data from multiple CSV files into a Line Chart (each file into its own LineSeries). This takes some time (rendering the GUI unusable) so I wanted to do this operation in a seperate thread and display a loading message (The BusyIndicator from the Extended WPF Toolkit).
Unfortunately when I try to create a LineSeries in the BackgroundWorker, I get an exception: "The calling thread must be STA, because many UI components require this." I'm trying to copy the GUI's chart, populate the copy, then it to the GUI's chart upon completion. So this shouldn't be trying to access a control from a different thread.
/// <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;
// ...
}
From what I've read elsewhere, its not possible to make a BackgroundWorker STA, so is there some other way I can load the chart with data without hanging the GUI?
Thanks
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您不应该从后台工作人员访问 UI 元素。 WPF UI(几乎)是单线程的。这意味着您必须在后台线程中加载数据,并在 UI 线程中调用所需的函数来更新 UI(使用 Dispatcher.Invoke)。
因此,基本上,您必须在后台线程中对数据进行所有必要的(且冗长的)准备(无论您使用 BackgroundWorker 还是任何其他众多多线程工具,这并不重要),但是创建UI 元素的管理和视觉状态的维护必须在主线程中
调用
。在这里您可以找到一个简单的示例。
You shouldn't access the UI elements from your background worker. The WPF UI is (almost) single-threaded. This means that you must load your data in the background thread, and for updating the UI call the needed function in the UI thread (using Dispatcher.Invoke).
So basically you have to do all the necessary (and lengthy) preparation of the data in your background thread (doesn't really matter whether you take a
BackgroundWorker
or any other of numerous multithreading facilities), but creation of UI elements and maintenance of the visual state has to beInvoke
d into the main thread.Here you can find a simple example.
您可以使用普通线程。然后你就拥有了使其成为 STA 的控制权(所有权)。
Bgw 仅添加了一些方便的功能来与 GUI 交互,没有什么是您自己用几行代码编写不了的。您甚至没有使用 UpdateProgress。
You could use a plain Thread. Then you have the control (ownership) to make it STA.
The Bgw adds just a few handy features to interact with the GUI, nothing you can't write yourself in a few lines of code. You're not even using the UpdateProgress.