如何使用不同的线程加载我的UserControl?

时间:2016-02-04 08:53:45

标签: c# .net wpf multithreading backgroundworker

我正在使用.NET C#并尝试加载包含三个选项卡的UserControl。我想要做的是,加载第一个Tab使其可见并响应,然后在后台线程(或工作人员或任务)中加载其他两个选项卡。

我尝试使用线程,任务,后台工作者但是总是有相同的异常“调用线程无法访问此对象,因为不同的线程拥有它。

这里我使用了一些代码示例:

BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerAsync();

在bw_DoWork中我把我需要做的所有工作都涉及到UI更新

Task t1 = new Task(DoWork);
t1.Start();

在DoWork中我也这样做(一些代码使用UI更新)

感谢您的帮助。

Oussema

2 个答案:

答案 0 :(得分:2)

这不是不可能的,但这是一个坏主意 - 要做到正确是非常棘手的。

相反,您希望将业务逻辑与UI分离,以便您可以在后台执行逻辑,而UI仍在UI线程上。关键是你不能从后台线程修改UI控件 - 而是首先加载你需要的任何数据,然后使用InvokeReportProgress将它封送回UI线程,您可以在哪里更新UI。

如果创建控件本身花费的时间太长,您可能需要查看一些选项以提高其性能。例如,使用BeginUpdate和朋友避免不必要的重绘和布局,同时您仍然填写数据,或按需创建控件(因为它们即将可见),而不是全部预先。

使用await的一个简单示例(使用Windows窗体,但基本思想无处不在):

tabSlow.Enabled = false;

try
{
  var data = await Database.LoadDataAsync();

  dataGrid.DataSource = data;
}
finally
{
  tabSlow.Enabled = true;
}

数据库操作在后台完成,完成后,UI更新将在UI线程上执行。如果您需要进行一些昂贵的计算,而不是I / O,那么您只需使用await Task.Run(...) - 只需确保任务本身不会更新任何UI;它应该只返回更新UI所需的数据。

答案 1 :(得分:0)

WPF不允许您从后台线程更改UI。只有ONE SINGLE线程可以处理UI线程。相反,您应该在后台线程中计算数据,然后调用Application.Current.Dispatcher.Invoke(...)方法来更新UI。例如:

Application.Current.Dispatcher.Invoke(() => textBoxTab2.Text = "changing UI from the background thread");