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, не может быть обновлен, поэтому вы не хотите этого делать.

Другие вопросы по тегам