Reputation: 2844
I'm trying to get background worker functioning in the most basic way with a windows form, for instance get a background process to change text in a label.. I got the basic background worker code here.. http://www.albahari.com/threading/part3.aspx Heres the code in my form.. Trying to make it so you press a button and then background worker thread is spawned which changes text in the label
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication4
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
BackgroundWorker _bw = new BackgroundWorker();
void backgroundio()
{
_bw.DoWork += bw_DoWork;
_bw.RunWorkerAsync("Message to worker");
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
// This is called on the worker thread
label1.Text = (string)(e.Argument); // writes "Message to worker"
// Perform time-consuming task...
}
void button1_Click(object sender, EventArgs e)
{
backgroundio();
}
}
}
For the label1.Text = (string)(e.Argument); I get this error.
Cross-thread operation not valid: Control 'label1' accessed from a thread other than the thread it was created on.
Thank for any help !! :)
Actually while I'm here can somebody explain this line?
_bw.DoWork += bw_DoWork;
I dont get how += makes any sense in this context. how can you add those things?
Upvotes: 2
Views: 11543
Reputation: 1
You can not update UI controls from DoWork
event. UI can be Updated from _bw_RunWorkerCompleted
.
Pass the values you want to update in UI elements from DoWork
event to RunWorkerCompleted
Even through event arguments and update the label in Completed event.
Upvotes: 0
Reputation: 192457
Your bw_doWork
method is a static. This means there is only one of those methods for all instances of your class. That method cannot access instance-specific properties or fields or methods. That explains the compiler error.
If you change that method to NOT be static it will allow you to reference label1
within it.
The syntax you refer to is a shortcut to add an event handler to that event.
It just means "Add this handler to the list of handlers for the given event." The long-handed way of doing it is with AddEventHandler.
http://msdn.microsoft.com/en-us/library/system.reflection.eventinfo.addeventhandler.aspx
The cryptic message you get at runtime indicates that you cannot update the UI objects on the non-uI thread. (The bg worker implies a different thread.) The solution is to perform the update you want on the UI thread.
void bw_DoWork(object sender, DoWorkEventArgs e)
{
// This is called on the worker thread
UpdateLabel((string)e.Argument));
...more work here...
}
void UpdateLabel(string s)
{
if (this.label1.InvokeRequired)
{
// It's on a different thread, so use Invoke.
this.BeginInvoke (new MethodInvoker(() => UpdateLabel(s));
}
else
{
// It's on the same thread, no need for Invoke
this.label1.Text = s;
}
}
To learn more about it, http://msdn.microsoft.com/en-us/library/ms171728(v=vs.90).aspx
Upvotes: 9
Reputation: 3997
In the DoWork-event you should perform the work you want to be executed on a different thread, ie. "the background work that won't freeze your application".
Then you have a Progress-event (I think, something similar at least) - this is where you update your GUI, ie. change the text or your label. Don't perform any heavy work here, it's your main thread.
In your DoWork-event, ie. the background thread, you may report the progress to the main thread using a method on your BackgroundWorker object (I don't remember the name of the method, something like Report, Progress or something), the Progress-event will then be called on the main thread.
Upvotes: 2