Come implementare correttamente un BackgroundWorker con gli aggiornamenti di ProgressBar?

-Aggiornato – 14/10 ha anche posto questa domanda

Per dare un’idea chiara di cosa sta succedendo e tenendo conto dei commenti e di questo articolo qui

Quello che voglio veramente fare ora è invocare un nuovo modulo con una barra di avanzamento su di esso e farlo girare e animare mentre il mio thread back ground esegue il mio lungo processo nel database e quindi richiama un evento di forma chiusa

Il lavoratore in background è impostato qui

public partial class MainWindow : Window { //Declare background workers BackgroundWorker bw = new BackgroundWorker(); BackgroundWorker bwLoadCSV = new BackgroundWorker(); BackgroundWorker bwProgressBar = new BackgroundWorker(); 

Quindi delegati aggiunti qui

  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 chiamata viene effettuata qui dalla class della finestra principale

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

Il lavoratore in background lavora è

 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. 

Per segnalare il lavoro in background, lo faccio. Questo è dove sto provando a caricare una nuova chiamata di modulo ProgressDialog che ha una barra di avanzamento su di esso, che sto provando impostata su Indeterminable in modo che “si spalmi” attraverso il mio modulo ProgressDialoge per mostrare all’utente che qualcosa è ancora in corso. Ho usato la parte reporter del lavoro in background perché credo che abbia accesso al thread della finestra principale e spero che il metodo invoke venga chiamato dal thread della finestra principale, ma non sono molto sicuro

Ecco il giornalista

  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() + "%"; }); } } 

Infine il xaml per il modulo ProgressDialog e la sua class

      

class

 ///  /// Interaction logic for ProgressDialog.xaml ///  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; } } )); } } } 

Ho letto e fatto una serie di tutorial su background worker e anche su alcuni thread ma non riesco a ottenere il risultato che voglio

L’idea è che ho due lunghi processi in cui sto ricevendo un clone datatable dal mio bd remoto o sto aggiornando il db dalla mia applicazione wpf (.net 4). Mentre il processo è in esecuzione, desidero un controllo della barra di avanzamento e l’aggiornamento, per la ragione ovvia di chiarire che alcuni lavori sono in corso. Così ho fatto le solite routine di avanzamento del report nel worker in background e funziona … Tuttavia, nel mio thread dowork ho questo comando

 CHHoursDataProvider CH = new CHHoursDataProvider(); oTable = CH.CloneCHHours(); 

questo dove la comunicazione con db è e questo comando richiede un buon 60-90 sec su una connessione remota VPN, quindi anche se lo faccio

 CHHoursDataProvider CH = new CHHoursDataProvider(); worker.ReportProgress((10)); oTable = CH.CloneCHHours(); worker.ReportProgress((20)); 

La finestra principale sembra ancora congelata e come se si fosse schiantato!

Quindi tutto quello che voglio fare è che all’inizio della chiamata al lavoro in background è impostata una barra di avanzamento in esecuzione e lasciarla in esecuzione fino alla fine dell’attività. Questo è tutto ciò che devo fare per finire il mio primo progetto in assoluto e dopo tre giorni non riesco ancora a capirlo!

Quindi ho provato il seguito

Nel bw i progressi sono cambiati e nella class della finestra principale

  this.progressBar2.IsIndeterminate = true; 

Tuttavia, l’animazione non viene avviata fino a quando il thread di Dowork non è terminato.

Ho quindi creato un altro background worker per aggiornare la progressbar2, che era collegata a un pulsante nella finestra principale era ok, ma non appena ho provato a usarlo dall’altra worker in background o dalla class della finestra principale non è stato eseguito fino alla dowork thread è stato completato sul primo worker in background

Ho quindi provato a seguire un metodo di richiamo ma DAVVERO mi sono perso!

Quindi chiunque può aiutare posso immaginare che abbia qualcosa a che fare con il threading e il lavoro sul thread sbagliato ecc, ma quello che faccio a questo proposito non ne ho idea.

Posso pubblicare più codice se necessario

Ian

Dato che non hai mostrato il codice completo di BackgroundWorker , non posso dire se l’hai implementato correttamente. Come tale, tutto ciò che posso fare è mostrarvi un semplice esempio funzionante di aggiornamento di un controllo ProgressBar :

UserControl XAML:

    

MainWindow XAML:

    

Codice UserControl dietro:

 using System.ComponentModel; using System.Threading; using System.Windows; using System.Windows.Controls; namespace WpfApplication1.Views { public partial class TestView : UserControl { private BackgroundWorker backgroundWorker = new BackgroundWorker(); public TestView() { InitializeComponent(); backgroundWorker.WorkerReportsProgress = true; backgroundWorker.ProgressChanged += ProgressChanged; backgroundWorker.DoWork += DoWork; // not required for this question, but is a helpful event to handle backgroundWorker.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted; } private void UserControl_Loaded(object sender, RoutedEventArgs e) { backgroundWorker.RunWorkerAsync(); } private void DoWork(object sender, DoWorkEventArgs e) { for (int i = 0; i <= 100; i++) { // Simulate long running work Thread.Sleep(100); backgroundWorker.ReportProgress(i); } } private void ProgressChanged(object sender, ProgressChangedEventArgs e) { // This is called on the UI thread when ReportProgress method is called progressBar.Value = e.ProgressPercentage; } private 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 } } }