Depois de executar o BackgroundWorker () com sucesso, o resto do programa não é executado
Estou usando um BackgroundWorker () para executar uma consulta de longa duração em segundo plano enquanto estou apresentando uma janela pop-up informando que minha execução está em execução.
Aqui está como eu chamo o bg_worker ()
using System.Windows;
using System.ComponentModel;
using System.Threading;
using System;
using System.IO;
using System.Data.SqlClient;
using System.Windows.Input;
namespace TestEnvironment
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class ProgressBarTemplate : Window
{
private CreateProjectScreen _CreateProjectScreen;
private LoginScreen _LoginScreen;
public ProgressBarTemplate()
{
InitializeComponent();
}
public static int RunCalculationsMethod(string connectionstring, string foldername)
{
bool exists = Directory.Exists(foldername);
if (!exists)
{
Directory.CreateDirectory(foldername);
}
try
{
using (SqlConnection sqlConnection = new SqlConnection(connectionstring))
{
var calculations_query = "SELECT * FROM table1");
using SqlCommand sqlCommand = new SqlCommand(calculations_query, sqlConnection);
sqlConnection.Open();
sqlCommand.CommandTimeout = 60 * 10;
int NumbderOfRecords = sqlCommand.ExecuteNonQuery();
return NumbderOfRecords;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error);
return -100;
}
}
private void Window_ContentRendered(object sender, EventArgs e)
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted;
worker.RunWorkerAsync();
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
int IsSuccessful = RunCalculationsMethod("Server=localhost;Database=DB_Name;Integrated Security=SSPI", String.Format("C:\\folder_path\\"));
}
void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// This is called on the UI thread when the DoWork method completes
// so it's a good place to hide busy indicators, or put clean up code
try
{
this.Close();
MessageBox.Show("DQ Calculations completed successfully", "Information", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
}
O código acima é colocado em uma janela chamada ProgressBarTemplate ()
E o que eu quero é chamar o background_worker no clique do botão, de um botão colocado na minha MainWindow
Então minha MainWindow tem o seguinte clique no botão
private void RunCalculationsButton_Click(object sender, RoutedEventArgs e)
{
//RunCalculationsMethod(SQLServerConnectionDetails(), String.Format("C:\\DQ_Findings_{0}", HomePageTab.Header.ToString().Split(" - ")[1]));
try
{
Application.Current.Dispatcher.Invoke((Action)delegate
{
ProgressBarTemplate win_progressbar = new ProgressBarTemplate();
win_progressbar.Show();
//RunCalculationsMethod(SQLServerConnectionDetails(), String.Format("C:\\DQ_folder_test\\Findings\\"));
}); // The code runs up to this point.
//The code below is not executed for a reason, which I am trying to solve with this question
List<SucessfulCompletion> reportsucessfulcompletion = new List<SucessfulCompletion>();
reportsucessfulcompletion = SuccessfulCalculationsTimestamp(SQLServerConnectionDetails());
if (reportsucessfulcompletion[0].Result==1)
{
//Enable is only if successfull
PreviewCalculationsButton.IsEnabled = true;
PreviewReportButton.IsEnabled = true;
//add textbox of sucess
TickButtonSymbolCalculations.Visibility = Visibility.Visible;
SQLSuccessfulTextCalculations.Visibility = Visibility.Visible;
XerrorSymbolCalculations.Visibility = Visibility.Hidden;
SQLFailedTextCalculations.Visibility = Visibility.Hidden;
SQLSuccessfulTextCalculations.Text = String.Format("Completed On: {0}", reportsucessfulcompletion[0].Timestampvalue);
}
else
{
//add textbox of fail
TickButtonSymbolCalculations.Visibility = Visibility.Hidden;
SQLSuccessfulTextCalculations.Visibility = Visibility.Hidden;
XerrorSymbolCalculations.Visibility = Visibility.Visible;
SQLFailedTextCalculations.Visibility = Visibility.Visible;
SQLFailedTextCalculations.Text = String.Format("Failed On: {0}", reportsucessfulcompletion[0].Timestampvalue);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
}
Ao clicar no botão, inicio o bg_worder chamando a janela ProgressBarTemplate (). Embora após completar a tarefa o código gere algum texto e habilite a visibilidade de alguns botões eles não são executados. Por que isso está acontecendo? Estou esquecendo de algo?
Respostas
O código é um pouco confuso, então vou postar como deve ser feito.
BGW é uma classe obsoleta totalmente substituída por Task.Runand Progress Invokequando async/awaitestiverem disponíveis.
Em vez de colocar os cálculos na janela de progresso, você pode usar um manipulador de eventos assíncrono, realizar quaisquer cálculos em segundo plano e atualizar a IU assim que a operação em segundo plano for concluída. O formulário de progresso não parece relatar nenhum progresso, então tudo o que é necessário é exibi-lo e ocultá-lo. O código pode ser tão simples como:
private async void RunCalculationsButton_Click(object sender, RoutedEventArgs e)
{
var win_progressbar = new ProgressBarTemplate();
win_progressbar.Show();
try
{
//Update the UI
var results=await Task.Run(()=> RunCalculationsMethod(...));
//Update the UI
}
finally
{
win_progressbar.Close();
}
}
O try/finallyé usado para garantir que o formulário seja fechado mesmo se houver um erro.
Relatório de progresso
O relatório de progresso está disponível por meio da classe Progress e da interface IProgress . IProgress<T>permite que uma tarefa em segundo plano envie uma mensagem fortemente tipada para a classe que implementa a interface. A Progress<T>implementação garante que a mensagem seja processada no encadeamento onde foi criada, normalmente o encadeamento de UI.
Supondo que este seja o tipo de mensagem:
class MyProgress
{
public int Percent{get;set;}
public string Message {get;set;}
}
O RunCalculationsMethodpode ser modificado para aceitar e usar um IProgress<MyProgress>;
public static int RunCalculationsMethod(string connectionstring, string foldername,
IProgress<MyProgress> progress)
{
progress.Report(new MyProgress{Percent=0,Message="Starting"};
....
progress.Report(new MyProgress{Percent=100,Message="Finished"};
}
O manipulador de eventos só precisa criar um Progress<MyProgress>e fornecer um método para atualizar a IU. Vamos supor que ProgressBarTemplatetenha esse método, chamado Update(string,int):
private async void RunCalculationsButton_Click(object sender, RoutedEventArgs e)
{
var win_progressbar = new ProgressBarTemplate();
IProgress<MyProgress> pg=new Progress<MyProgress>(pg=>
win_progressbar.Update(pg.Message,pg.Percent));
win_progressbar.Show();
try
{
//Update the UI
var results=await Task.Run(()=> RunCalculationsMethod(...,pg));
//Update the UI
}
finally
{
win_progressbar.Close();
}
}
Você pode encontrar uma explicação mais completa em Async em 4.5: Habilitando o andamento e o cancelamento em APIs Async
Você deve usar Application.Current.Dispatcher.Invoke porque tenta acessar o elemento de interface do usuário. Exceto o thread principal, outro thread não pode acessar o elemento ui.
Substitua seu código para
Dispatcher.Invoke(() =>{
// UI access code
});