BackgroundWorker读取数据库和更新GUI

当我查询数据库并在BackgroundWorker中的DoWork中更新DataGrid ,我试图保持我的不确定ProgressBar动画顺利运行。 我知道我无法在DoWork更新UIThread,所以我使用Dispatcher.BeginInvoke来访问DataGrid ,但我仍然收到了由另一个线程拥有的exception。

问题1

为什么会这样? DoWork应该在UIThread拥有的BackgroundWorker的线程上,Dispatcher应该允许我的DoWork访问我的GUI。

进一步研究BackgroundWorker建议应该在ProgressChanged()处理UI操作。 但是,如果我将所有数据库/ GUI操作移动到ProgressChanged()并在DoWork() Thread.Sleep(5000)上放置一个Thread.Sleep(5000)来模拟某些东西,以便为ProgressChanged()提供足够的时间来运行,那么GUI就不会被更新,虽然ProgressBar继续其不确定的平滑动画。

问题2

我在BackgroundWorker线程上调用了Thread.Sleep(5000)。 ProgressChanged()应该花5秒时间查询数据库并更新GUI。 为什么不?

XAML:

    

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) { } 

DoWork事件处理程序中创建项集合,然后在完成在后台线程上获取数据后,在RunWorkerCompleted事件处理程序中将DataGridItemsSource属性设置为此属性:

 private void btnSearch_Click(object sendoer, RoutedEventArgs e) { bgw.WorkerReportsProgress = true; bgw.ProgressChanged += ProgressChanged; bgw.DoWork += DoWork; bgw.RunWorkerCompleted += BGW_RunWorkerCompleted; myProgressBar.Visibility = Visibility.Visible; bgw.RunWorkerAsync(); } private void DoWork(object sender, DoWorkEventArgs e) { List results = new List(); 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()) { results.Add(new { Id = rdr.GetInt32(0), Name = rdr.GetString(1).ToString(), }); } } } } e.Result = results; } private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { myDataGrid.ItemsSource = e.Result as List; myProgressBar.Visibility = Visibility.Collapsed; } 

您需要调用BackgroundWorkerReportProgress方法来报告任何进度,但是报告数据库检索操作的进度是没有意义的,因为您无论如何都不知道实际的进度。 你最好只使用一个不确定的ProgressBar ,并在你开始操作时显示它,并在操作完成后隐藏它,如上面的示例代码所示。

Thread.Sleep将阻止当前线程。 如果阻止UI线程,则无法更新UI(包括ProgressBar),因此您不希望这样做。