¿Cómo implementar correctamente un BackgroundWorker con actualizaciones de ProgressBar?
-Actualizado - 14/10 También pregunté estopregunta
Dar una idea clara de lo que está sucediendo y teniendo en cuenta los comentarios y de este artículo.aquí
Lo que realmente quiero hacer ahora es invocar un nuevo formulario con una barra de progreso y hacer que se ejecute y anime mientras mi hilo de fondo ejecuta mi largo proceso hacia la base de datos y luego invoca un evento de formulario cerrado.
El trabajador de fondo se configura aquí
public partial class MainWindow : Window
{
//Declare background workers
BackgroundWorker bw = new BackgroundWorker();
BackgroundWorker bwLoadCSV = new BackgroundWorker();
BackgroundWorker bwProgressBar = new BackgroundWorker();
Entonces los delegados añadieron aquí
public MainWindow()
{
bwLoadCSV.WorkerReportsProgress = true;
bwLoadCSV.WorkerSupportsCancellation = true;
bwLoadCSV.DoWork += new DoWorkEventHandler(bwLoadCSV_DoWork);
bwLoadCSV.ProgressChanged += new ProgressChangedEventHandler(bwLoadCSV_ProgressChanged);
bwLoadCSV.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwLoadCSV_RunWorkerCompleted);
La llamada se hace aquí desde la clase de la ventana principal.
private void CSV_Load_Click(object sender, RoutedEventArgs e)
///Function to read csv into datagrid
///
{
//Turn Cursor to wait
System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.WaitCursor;
//Test connection to sql server
if (CHHoursDataProvider.IsDatabaseOnline() == false)
{
System.Windows.Forms.MessageBox.Show("Can not establish contact with sql server" + "\n" + "Contact IT", "Connection Error");
//Set UI picture
return;
}
//Set a control to update the user here
tbLoadDgStat.Visibility = Visibility.Visible;
tbLoadDgStat.Text = "Getting data templete from Database...";
string FilePath = txFilePath.Text;
if (bwLoadCSV.IsBusy != true)
{
//load the context object with parameters for Background worker
bwCSVLoadContext Context = new bwCSVLoadContext();
Context.Site = cBChSite.Text;
Context.FilePath = txFilePath.Text;
Context.FileName = fileTest;
Context.Wageyear = cbWageYear.Text;
Context.Startdate = ((DateTime)dpStartDate.SelectedDate);
Context.Enddate = ((DateTime)dpEndDate.SelectedDate);
bwLoadCSV.RunWorkerAsync(Context);
}
El trabajador de fondo trabaja es
private void bwLoadCSV_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
bwCSVLoadContext Context = e.Argument as bwCSVLoadContext;
worker.ReportProgress((10));
if ((worker.CancellationPending == true))
{
e.Cancel = true;
}
else
{
// Perform a time consuming operation and report progress load csv into datagrid.
Para informar el trabajo de fondo hago esto. Aquí es donde estoy tratando de cargar un nuevo formulario llamado ProgressDialog que tiene una barra de progreso, que estoy configurada como Indeterminable, por lo que simplemente se "balancea" a través de mi formulario ProgressDialoge para mostrar al usuario que algo está sucediendo. He utilizado la parte de reportero del trabajo en segundo plano porque creo que tiene acceso al subproceso de la ventana principal y espero que el método de invocación se llame desde el subproceso de la ventana principal, pero no estoy realmente seguro
Aquí está el reportero.
private void bwLoadCSV_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.Dispatcher.Invoke((MethodInvoker)delegate { tbLoadDgStat.Visibility = Visibility.Visible; });
//tbLoadDgStat.Visibility = Visibility.Visible;
//this.progressBar1.Value = e.ProgressPercentage;//This works but pauses on long steps
if (e.ProgressPercentage == 10)
{
//Try to open a new form with a class ProgressDialog and set the progressbar
// on the frm to IsIndeterminate=true
//THIS IS NOT WORKING
this.Dispatcher.BeginInvoke (new Action(() =>
{ ProgressDialog progressDialog = new ProgressDialog();
progressDialog.SetIndeterminate(true);
}));
//this updates the main form OK
this.Dispatcher.Invoke((MethodInvoker)delegate { tbLoadDgStat.Text = "Getting data templete from Database..."; });
}
else if (e.ProgressPercentage == 20)
{
this.Dispatcher.Invoke((MethodInvoker)delegate { tbLoadDgStat.Text = "Data template retrieved..."; });
}
else
{
if (e.ProgressPercentage % 10 == 0)
{
this.Dispatcher.Invoke((MethodInvoker)delegate { tbLoadDgStat.Text = "Adding Data..." + e.ProgressPercentage.ToString() + "%"; });
}
}
Por último, el xaml para el formulario ProgressDialog y su clase
<Window x:Class="Test_Read_CSV.ProgressDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Progress Dialog" Height="115" Width="306" Name="ProgressPopup">
<Grid>
<ProgressBar Height="31" HorizontalAlignment="Left" Margin="12,33,0,0" Name="progressBar1" VerticalAlignment="Top" Width="250" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="7,4,0,0" Name="tbEvent" VerticalAlignment="Top" Width="254" IsReadOnly="True" IsEnabled="False" />
</Grid>
clase
/// <summary>
/// Interaction logic for ProgressDialog.xaml
/// </summary>
public partial class ProgressDialog : Window
{
public ProgressDialog()
{
WindowStartupLocation = WindowStartupLocation.CenterScreen;
InitializeComponent();
}
public ProgressDialog(String Purpose)
{
InitializeComponent();
tbEvent.Text = Purpose;
WindowStartupLocation = WindowStartupLocation.CenterScreen;
}
public void UpdateProgress(int progress)
{
progressBar1.Dispatcher.BeginInvoke(
new Action(() =>
{
progressBar1.Value = progress;
}
));
}
public void SetIndeterminate(bool isIndeterminate)
{
progressBar1.Dispatcher.BeginInvoke(
new Action(() =>
{
if (isIndeterminate)
{
progressBar1.IsIndeterminate = true;
}
else
{
progressBar1.IsIndeterminate = false;
}
}
));
}
}
}
He leído y realizado una serie de tutoriales sobre trabajo en segundo plano e incluso algunos en subprocesos, pero parece que no puedo obtener el resultado que deseo
La idea es que tengo dos procesos largos en los que obtengo un clon de datos de mi bd remoto o estoy actualizando la base de datos de mi aplicación wpf (.net 4). Mientras el proceso se está ejecutando, quiero un control de la barra de progreso y actualizarlo, por la razón obvia de dejar en claro que se está realizando algún trabajo. Así que hice las rutinas de progreso de informe habituales en el trabajador de fondo y funciona ... Sin embargo, en mi hilo de dowork tengo este comando
CHHoursDataProvider CH = new CHHoursDataProvider();
oTable = CH.CloneCHHours();
Esto es donde la comunicación con db es y este comando toma unos buenos 60 - 90 segundos en una conexión remota vpn, así que incluso si hago esto
CHHoursDataProvider CH = new CHHoursDataProvider();
worker.ReportProgress((10));
oTable = CH.CloneCHHours();
worker.ReportProgress((20));
¡La ventana principal todavía se ve congelada y se ha estrellado!
Así que todo lo que quiero hacer es al inicio de la llamada al trabajo en segundo plano establecer una barra de progreso en ejecución y dejarla en ejecución hasta el final de la tarea. ¡Esto es todo lo que necesito hacer para terminar mi primer proyecto y después de tres días todavía no puedo entenderlo!
Así que he intentado el seguimiento
En el progreso de bw cambiado y en la clase de ventana principal.
this.progressBar2.IsIndeterminate = true;
Sin embargo, la animación no comienza hasta que el hilo de Dowork ha finalizado.
Luego creé otro trabajador de fondo para actualizar el progressbar2, que estaba vinculado a un botón en la ventana principal estaba bien, pero tan pronto como intenté usarlo desde el otro trabajador de fondo o desde la clase de la ventana principal no funcionó hasta el dowork hilo había completado en el primer trabajador de fondo
Luego traté de seguir un método de invocación, ¡pero REALMENTE me perdí en eso!
Entonces, ¿alguien puede ayudarme? Puedo adivinar que es algo que ver con subprocesos y trabajar en el subproceso incorrecto, etc. Pero no tengo idea de lo que hago al respecto.
Puedo publicar más código según sea necesario
Ian