Reputation: 616
I have service call in my form application. When my button has been clicked I am calling my service method. Here is my code block:
void listBox_SelectedValueChanged(object sender, EventArgs e)
{
if (listBox.SelectedItem.Equals("Demo"))
{
progressBar1.Visible = true;
backgroundWorker1.RunWorkerAsync();
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
ObservableCollection<FilterInfo> filterInfos = new ObservableCollection<FilterInfo>();
FilterInfo myFilterObj = new FilterInfo("SUPERVISOR", userName);
filterInfos.Add(myFilterObj);
ObservableCollection<DEMOBec> demos = demoSvc.GetDemoByFilter(filterInfos, false);
dt = new Converter<DEMOBec>().ConvertDataTable(demos.ToList());
}
Before calling the service, I make my ProgressBar (Style = Marquee) visible. But I couldn't make it invisible in completed method because of Cross Thread problem.
When I tried to do something with in UI thread in BGWorker's Completed event,
void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progressBar1.Visible = false;
}
I am getting an exception :
Cross-thread operation not valid: Control 'Form1' accessed from a thread other than the thread it was created on.
How can I handle this problem?
Upvotes: 1
Views: 5329
Reputation: 185
Here's alittle piece of code that I always love to use, I don't remember where I got it from I put it in my Dropbox a long time ago.
public static class ControlExtensions
{
public static TResult InvokeEx<TControl, TResult>(this TControl control,
Func<TControl, TResult> func)
where TControl : Control
{
return control.InvokeRequired
? (TResult)control.Invoke(func, control)
: func(control);
}
public static void InvokeEx<TControl>(this TControl control,
Action<TControl> func)
where TControl : Control
{
control.InvokeEx(c => { func(c); return c; });
}
public static void InvokeEx<TControl>(this TControl control, Action action)
where TControl : Control
{
control.InvokeEx(c => action());
}
}
Usage
this.InvokeEx( x => Progressbar1.Visible = false); //Note 'x' could be any variable
This way you won't have to type all that out each time, and it's so simple to use. Just add this right to the end of your form class (after the last bracket closes). You can use this to preform any cross thread operations safely.
Upvotes: 2
Reputation: 1911
I think the exception isn't raised in the RunWorkerCompleted
event (as in comments said). My suggestion is, that the lines you try to access UI cmponents in the DoWork
event fires it.
Try to put the data you need in the DoWork
event into DoWorkEventArgs e
. Tons of examples how to do it are provided on SO like this discussion.
When the work has finished, provide the "main UI thread" the modified/new data by using event args, too, like this:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) {
// do some work
e.Result = ... ; // modified/new data
}
and retrieve it in the following way:
void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
var newData = (CastToWhatever) e.Result;
}
Hope it helps =)
EDIT
Your code in backgroundWorker1_RunWorkerCompleted()
is definitly not the source of the exception. It built a little and simple example to demonstrate my argment:
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync(textBox1.Text);
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
var num = Int32.Parse(e.Argument.ToString()) + 1;
backgroundWorker1.ReportProgress(num);
e.Result = num;
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = (int)e.ProgressPercentage;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
textBox1.Text = e.Result.ToString();
}
What it does? On the WinForm there is a TextBox
with a number, a ProgressBar
and a Button
to start a BackGroundWorker
.
The worker gets the number from the TextBox
(UI thread), DoWork
increases it by 1
and saves it as result (new thread). Additionally it reports the increased number to the ProgressBar
(cross-thread).
Finally RunWorkerCompleted
reads out the result and stores the new value in the TextBox
(UI thread).
So, please re-check your code ;-)
Upvotes: -1
Reputation: 63
Modify the Visibility of the ProgressBar on the UI thread.
Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)
delegate()
{
progressBar1.Visible = false;
});
Upvotes: 0
Reputation: 352
You need to use Invoke() method
private delegate void ToDoDelegate();
void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Invoke(new ToDoDelegate(() => progressBar1.Visible = false));
}
more info for Invoke() http://msdn.microsoft.com/de-de/library/System.Windows.Forms.Control.Invoke(v=vs.110).aspx
Upvotes: 4