BackgroundWorker для чтения базы данных и обновления графического интерфейса
Я пытаюсь сохранить свою неопределенность ProgressBar
анимация работает плавно, когда я запрашиваю базу данных и обновляю DataGrid
в DoWork
в BackgroundWorker
, Я понимаю, что не могу обновить UIThread в DoWork
вот и пользуюсь Dispatcher.BeginInvoke
чтобы получить доступ DataGrid
Тем не менее я все еще получаю исключение, принадлежащее другому потоку.
Вопрос 1
Почему это происходит? DoWork должен находиться в потоке BackgroundWorker, принадлежащем UIThread, а Dispatcher должен разрешать моему DoWork доступ к моему графическому интерфейсу.
Если заглянуть дальше в BackgroundWorker, предположим, что пользовательский интерфейс должен обрабатываться в ProgressChanged()
, Но если я переместить все операции базы данных / графического интерфейса в ProgressChanged()
и положить Thread.Sleep(5000)
на DoWork()
подражать чему-то, над чем работать, чтобы дать достаточно времени для ProgressChanged()
чтобы запустить, графический интерфейс не обновляется, хотя ProgressBar
продолжить свою неопределенную плавную анимацию.
вопрос 2
Я вызвал Thread.Sleep(5000) в потоке BackgroundWorker. 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)
{
}
1 ответ
Создайте коллекцию предметов в вашем DoWork
обработчик событий, а затем установите ItemsSource
собственность DataGrid
к этому в RunWorkerCompleted
обработчик событий, когда вы закончите извлекать данные в фоновом потоке:
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<object> results = new List<object>();
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<object>;
myProgressBar.Visibility = Visibility.Collapsed;
}
Вам нужно позвонить ReportProgress
метод BackgroundWorker
чтобы сообщалось о любом прогрессе, но довольно бессмысленно сообщать о ходе операции извлечения из базы данных, так как вы все равно не имеете ни малейшего представления о реальном прогрессе. Вам лучше использовать неопределенный ProgressBar
и показать его при запуске операции и скрыть ее после завершения операции, как показано в примере кода выше.
А также Thread.Sleep
заблокирует текущий поток Если вы заблокируете поток пользовательского интерфейса, ваш пользовательский интерфейс, включая ProgressBar, не может быть обновлен, поэтому вы не хотите этого делать.