【发布时间】:2017-11-08 14:31:41
【问题描述】:
当我查询数据库并更新 DoWork 中的 DataGrid 和 BackgroundWorker 时,我试图让我不确定的 ProgressBar 动画顺利运行。我知道我无法更新 DoWork 中的 UIThread,所以我使用 Dispatcher.BeginInvoke 访问 DataGrid,但我仍然收到 own-by-another-thread 异常。
问题一
为什么会这样? DoWork 应该打开 BackgroundWorker 的线程归 UIThread 拥有,Dispatcher 应该 允许我的 DoWork 访问我的 GUI。
进一步研究 BackgroundWorker 建议 UI 操作应在 ProgressChanged() 中处理。但是,如果我将所有数据库/GUI 操作移至 ProgressChanged() 并在 DoWork() 上放置 Thread.Sleep(5000) 来模拟一些工作,以便为 ProgressChanged() 运行,GUI 没有更新,尽管ProgressBar 继续其不确定的平滑动画。
问题 2
我在 BackgroundWorker 线程上调用了 Thread.Sleep(5000)。 ProgressChanged() 应该花 5 秒查询数据库和 更新图形用户界面。为什么不呢?
XAML:
<DataGrid x:Name="myDataGrid" ScrollViewer.CanContentScroll="True"
EnableRowVirtualization="True" EnableColumnVirtualization="True"
VirtualizingPanel.IsContainerVirtualizable="True"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.IsVirtualizingWhenGrouping="True"
Height="500" MaxHeight="500" />
<ProgressBar x:Name="myProgressBar" Height="20" Width="400"
IsIndeterminate="True" Visibility="Visible" />
<Button x:Name="mySearch" Click="btnSearch_Click">Search</Button>
C#
private BackgroundWorker bgw = new BackgroundWorker();
private void btnSearch_Click(object sendoer, RoutedEventArgs e)
{
bgw.WorkerReportsProgress = true;
bgw.ProgressChanged += ProgressChanged;
bgw.DoWork += DoWork;
bgw.RunWorkerCompleted += BGW_RunWorkerCompleted;
bgw.RunWorkerAsync();
}
private void DoWork(object sender, DoWorkEventArgs e)
{
// Thread.Sleep(5000);
using (SqlConnection conn = new SqlConnection(connStr))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand("SELECT * FROM " + MyTableName, conn))
{
using (SqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
Dispatcher.BeginInvoke(new Action(() =>
{
myDataGrid.Items.Add(new
{
Id = rdr.GetInt32(0),
Name = rdr.GetString(1).ToString(),
});
}));
}
}
}
}
}
private void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// Paste code from DoWork and uncomment Thread.Sleep(5000)
}
private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
}
【问题讨论】:
-
GUI(即 DataGrid)更新时在 Dispatcher 中引发异常。如果操作在 DoWork() 中,我会得到“调用线程无法访问此对象,因为不同的线程拥有它。”例外。
-
请参考我的回答。
-
哪个对象拥有您正在使用的
Dispatcher?它是否与myDataGrid所在的线程相关联?您可以尝试使用与您尝试访问的对象关联的调度程序,这是一种很好的做法。在你的情况下,这将是myDataGrid.Dispatcher.BeginInvoke(...)。 -
在您的
DoWork方法中使用Invoke而不是BeginInvoke。这样,它将触发 UI 线程上的操作。但是,我可能会将结果缓存到 100 个结果,然后调用 UI 线程,因为您已经在使用Virtualization。 -
请解释反对意见,以及为什么不鼓励提出这个问题?我没有发现类似的重复,我相信我以寻求理解而不是寻求解决方案的方式正确地表达了我的问题。
标签: c# wpf backgroundworker