Problem med C#.NET Background Worker

Permalänk
Medlem

Problem med C#.NET Background Worker

Hej

Jobbar lite med ett Word Add-in som ska förenkla utskrifter samt ha fackhantering.

Det jag vill göra är en sökning på skrivaren och lista alla tillgängliga fack skrivardrivrutiner erbjuder, denna process kan ta en liten tid därför hade jag tänkt att låta den gå i en backgroundworker för att inte få GUI att totallåsa sig.

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { int rawKind; PrintDocument p = new PrintDocument(); p.PrinterSettings.PrinterName = e.Argument.ToString(); List<string> items = new List<string>(); for (int j = 0; j < p.PrinterSettings.PaperSources.Count; j++) { rawKind = Convert.ToInt32(p.PrinterSettings.PaperSources[j].GetType().GetField("kind", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(p.PrinterSettings.PaperSources[j])); items.Add(p.PrinterSettings.PaperSources[j].SourceName.Trim()); backgroundWorker1.ReportProgress(Convert.ToInt32(j * 100 / p.PrinterSettings.PaperSources.Count)); if (backgroundWorker1.CancellationPending) { e.Cancel = true; break; } } e.Result = items; } private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) { //Här får jag felet! toolStripProgressBar1.Value = e.ProgressPercentage; } private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (e.Cancelled) { MessageBox.Show("Sökningen avbruten"); } else { MessageBox.Show("Sökningen klar"); } } private void searchTrayBtn_Click(object sender, EventArgs e) { if (!backgroundWorker1.IsBusy) { backgroundWorker1.RunWorkerAsync(printerListBox.SelectedItem.ToString()); searchTrayBtn.Text = "Avbryt"; } else { backgroundWorker1.CancelAsync(); } }

När koden anropar backgroundWorker1_ProgressChanged och ändrar värdet på toolStripProgressBar1 så hänger det sig och så får jag felmeddelandet

System.InvalidOperationException was unhandled Message=Åtgärden mellan trådar är inte giltig: Kontrollen hämtade från en annan tråd än den tråd som den skapades på. Source=System.Windows.Forms StackTrace: vid System.Windows.Forms.Control.get_Handle() vid System.Windows.Forms.Control.SendMessage(Int32 msg, Int32 wparam, Int32 lparam) vid System.Windows.Forms.ProgressBar.UpdatePos() vid System.Windows.Forms.ProgressBar.set_Value(Int32 value) vid System.Windows.Forms.ToolStripProgressBar.set_Value(Int32 value) vid Byråstöd.PrintSettingsForm.backgroundWorker1_ProgressChanged(Object sender, ProgressChangedEventArgs e) i C:\Users\andreas\Documents\Visual Studio 2010\Projects\Byråstöd\Byråstöd\PrintSettingsForm.cs:rad 84 vid System.ComponentModel.BackgroundWorker.OnProgressChanged(ProgressChangedEventArgs e) vid System.ComponentModel.BackgroundWorker.ProgressReporter(Object arg) vid System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object state) vid System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx) vid System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() vid System.Threading.ThreadPoolWorkQueue.Dispatch() vid System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() InnerException:

Det fungerar hur fint som helst om jag skippar progressbaren och andra UI grejjer. Men grejjen med backgroundsworkers är ju att man ska kunna köra en tråd i bakgrunden och ändå kunna påverka UI detaljer via events.

Permalänk
Medlem

Har du satt egenskapen WorkerReportsProgress till true på BackgroundWorker-kontrollen?

Annars kan du testa att anropa Invoke(). Den tar en delegat som parameter och exekverar den på tråden
som din UI-kontroll skapades på:

public partial class Form1 : Form { public delegate void UpdateProgressCallback(int progress); int progress = 0; public Form1() { InitializeComponent(); backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork); backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted); } public void UpdateProgress(int progress) { progressBar1.Value = progress; } private void button1_Click(object sender, EventArgs e) { backgroundWorker1.RunWorkerAsync(); } private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { for (int i = 0; i < 5; i++) { progress += 20; } this.Invoke(new UpdateProgressCallback(this.UpdateProgress), new object[]{ progress }); } private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { MessageBox.Show("Klart!"); } }

Visa signatur

Acer Predator Helios 18 Core i9 32GB 1024GB SSD RTX 4080 250Hz 18"

Permalänk
Medlem
Skrivet av Bit010:

Har du satt egenskapen WorkerReportsProgress till true på BackgroundWorker-kontrollen?

Annars kan du testa att anropa Invoke(). Den tar en delegat som parameter och exekverar den på tråden
som din UI-kontroll skapades på:

public partial class Form1 : Form { public delegate void UpdateProgressCallback(int progress); int progress = 0; public Form1() { InitializeComponent(); backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork); backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted); } public void UpdateProgress(int progress) { progressBar1.Value = progress; } private void button1_Click(object sender, EventArgs e) { backgroundWorker1.RunWorkerAsync(); } private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { for (int i = 0; i < 5; i++) { progress += 20; } this.Invoke(new UpdateProgressCallback(this.UpdateProgress), new object[]{ progress }); } private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { MessageBox.Show("Klart!"); } }

Ja WorkerReportsProgress är true. Jag förstår inte varför det inte fungerar. Skapar jag ett nytt projekt och skapar en Form klass så funkar backgroundworkern perfekt med olika UI detaljer. Får testa med Invoke.

Samma fel blir det i RunWorkerCompletedEvent metoden om jag har tex en progressbar som ska ändra värde tex om man vill nollställa den när jobbet är klart eller skicka resultatet till listbox osv. Då måste jag ju köra en Invoke där också, då kan man lika gärna skippa backgroundworkern helt och hållet.

Permalänk
Medlem

GUI-tråden måste själv göra ändringen på din progressbar.
För att hantera problematiken så kollar man InvokeRequired-propertyn.

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) { if (this.InvokeRequired) { this.Invoke(new MethodInvoker(() => backgroundWorker1_ProgressChanged(sender, e)) } else { toolStripProgressBar1.Value = e.ProgressPercentage; } }

Visa signatur

Sony Vaio FE21M

Permalänk
Medlem

Jag tänkte också först att invoke skulle vara sättet (som vanligt), så googlade och fick som trådskaparen upp exempel som liknar hans tokigt nog :S