I am currently trying to update a chart which is on my form to the background worker using:
bwCharter.RunWorkerAsync(chart1);
Which runs:
private void bcCharter_DoWork(object sender, DoWorkEventArgs e)
{
System.Windows.Forms.DataVisualization.Charting.Chart chart = null;
// Convert e.Argument to chart
//..
// Converted..
chart.Series.Clear();
e.Result=chart;
setChart(c.chart);
}
private void setChart(System.Windows.Forms.DataVisualization.Charting.Chart arg)
{
if (chart1.InvokeRequired)
{
chart1.Invoke(new MethodInvoker(delegate { setChart(arg); }));
return;
}
chart1 = arg;
}
However, at the point of clearing the series, an exception is thrown.
Basically, I want to do a whole lot more processing after clearing the series, which slows the GUI down completely - so wanted this in another thread.
I thought that by passing it as an argument, I should be safe, but apparently not!
Interestingly, the chart is on a tab page. I can run this over and over if the tabpage is in the background, but if I run this, look at the chart, hide it again, and re-run, it throws the exception. Obviously, it throws if the chart is in the foreground as well.
Can anyone suggest what I can do differently?
Thanks!
EDIT:
I know that this can be done in the form thread, as when I assign it back again. However the whole point of using a background worker is to avoid grinding the whole program to a halt. As I said, there is a lot more processing than just this one command.
I assumed that passing it as an argument would allow me to access it in that thread unhindered, but is there a chance that this passed chart still points back to the original chart in some way? If so, how can this be overcome?
I want to block the GUI thread as little as possible - so there seems no point in just invoking every command.
If you want to clear it first, then do a lot of asynchronous work before you redisplay it, why don't you call chart.Series.Clear(); before you invoke the BackgroundWorker? In that case it gets cleared on the main UI thread, then you perform som async work before you again set the chart from the UI thread.
Also, when using a BackgroundWorker, I would use the inbuilt ReportProgress and WorkerCompleted events to avoid the manual cross-thread invoking. That's part of the reason of using the BackgroundWorker in the first place to get this kind of functionality for "free". So setting the chart should be done in the WorkerCompleted to simplify your code (even if that is not the source of the problem in this case).
Check for Invoke Required in bcCharter_DoWork, if yes put the Clear method call in a delegate too.
if (InvokeRequired)
{
Invoke(new MethodInvoker(delegate
{
chart.Series.Clear();
e.Result=chart;
}));
return;
}
I agree with the analysis in the previous posts : you ask a thread to access to a resource of another thread.
Like you used a BackgroundWorker I suggest you to use the Dispatcher :
private void bcCharter_DoWork(object sender, DoWorkEventArgs e)
{
Chart chart = null;
Dispatcher.Invoke(DispatcherPriority.Normal,
new Action(() =>
{
chart.Series.Clear();
}));
}
The problem you are encountering is that you cannot access UI elements on threads other than the one they were created on. However, in your case you can simply clear the chart before calling the background worker.
You can manually marshal UI element access to the correct thread from another thread using Control.Invoke.
Related
my issue is the following:
I have a windows form in which I've placed a LayoutPanel, when the forms Loads, multiple controls like: textboxes and labels are being added to the LayoutPanel.
Then on a button click, I need to process the data entered by the user on those dynamically created controls. For that purpouse I use a Backgroundworker which is supposed to take those controls and read their data.
My issue es that the Backgroundworker doesn't allows me to access the control from the DoWork Method, but I need to do it that way because I'll be reporting the progress of the operations.
Here are portions of my code to clarify the concept:
private void frmMyForm_Load(object sender, EventArgs e)
{
//I add multiple controls, this one is just for example
LayoutPanel1.add(TextBox1);
....
}
private void bgwBackground_DoWork(object sender, DoWorkEventArgs e)
{
foreach (Control controlOut in LayoutPanel1.Controls)
{
//do some stuff, this one is just for example
string myString = controlOut.Name; //-> Here is the error, cant access controls from different Thread.
}
}
Setting text is simple just using a delegate, but how about getting the entire parent control to manipulate the child controls (just for getting info, I don't want to set any data, just need to Get Name, Text, stuff like that).
Hope I made myself clear, thank you all.
You can only access Windows Forms controls from the GUI thread. If you want to manipulate them from another thread, you will need to use the Control.Invoke method to pass in a delegate to execute on the GUI thread. In your situation, you should be able to do this:
private void bgwBackground_DoWork(object sender, DoWorkEventArgs e)
{
foreach (Control controlOut in LayoutPanel1.Controls)
{
this.Invoke(new MethodInvoker(delegate {
// Execute the following code on the GUI thread.
string myString = controlOut.Name;
}));
}
}
I like to define an extension method that allows me to use the cleaner lambda syntax:
// Extension method.
internal static void Invoke(this Control control, Action action) {
control.Invoke(action);
}
// Code elsewhere.
this.Invoke(() => {
string myString = controlOut.Name;
});
As you are already aware, accessing control values from any thread other than the UI thread is a big no-no. I'd say one reasonable implementation is to use a .NET synchronization mechanism, such as a WaitHandle, to suspend your background thread while the UI thread updates a thread-safe data structure of your choice.
The idea is that your background thread notifies the UI thread (via the delegate mechanism you are already familiar with) that it needs information, then waits. When the UI is finished populating the shared variable with information, it resets the WaitHandle, and the background worker resumes.
Without writing out and testing all the code, let me give you a few resources:
WaitHandle.WaitOne documentation with example usage: http://msdn.microsoft.com/en-us/library/kzy257t0.aspx
My own favorite method of invoking an event on the UI thread: http://www.notesoncode.com/articles/2009/01/24/PowerfulExtensionMethodsPart1.aspx
I am working on a WPF project where I have to import data from a lot of single files.
The actual importing of those files and the data in them is being done in a backgroundworker doWork method.
It works like a charm, does the job and updating a progress bar also works perfectly.
Now though, depending on what I encounter in those files, I occasionally need to get a decision from the User before I can proceed processing the current file.
What is the best way to open a window/dialog, getting the values set in there back into the backgroundworker.doWork method and continue processing?
Is that even possible with a backgroundworker or do I need to keep that processing logic in the main/UI thread and update the progress bar from there somehow?
I hope some of you can give me some hints or point me to other resources since I have not found much useful information for my specific problem.
Background worker works in a different thread. You can not invoke UI directly from a background thread. One way of achiving what you are trying to do is by using a flag and Dispatcher to invoke UI for user input
bool WaitFor_Input = false; //flag to indicate user input status
private void ThreadRun()
{
//THIS IS IN Background worker thread
//do task
WaitFor_Input = true;
//ask for user input
Dispatcher.BeginInvoke(new Action(Show_Dialogue), null);
while (WaitFor_Input); //Background worker waits untill user input is complete
//continue further processing
}
private void Show_Dialogue()
{
//THIS IS IN UI THREAD
//show your dialogue box here, update data variables
//finally set
WaitFor_Input = false;
}
Keeping processing logic in a background thread is actually a good idea.
You can call the ShowDialog of an OpenFileDailog class on a new Thread or BackgroundWorker( this will also create a new thread)
But when you want to pass or update any property or control that is running on the main thread you will need to use the Disptacher like this
Dispatcher.BeginInvoke(new Action(() => { YourMethodThatUpdatesSomething(); }));
I've got my main form Form1 running the main bulk of my program.
I have a separate thread started to perform an algorithm.
When I run the method from the new thread, method MyAlgorithm() I get the error
InvalidOperationException with the message, "Control control name accessed from a thread other than the thread it was created on."
How do I get back to the original thread so that I can run the method to update my text boxes with the latest values?
This is the method that I want to run contained in Form1, the main class in my application.
// Reset the results values
public void ShowResults()
{
while (true)
{
loopsNum.Text = Convert.ToString(resultLoopsNum);
nodesVisitedNum.Text = Convert.ToString(resultNodesVisitedNum);
nodesResolvedNum.Text = Convert.ToString(resultNodesResolvedNum);
cpuLoopsNum.Text = Convert.ToString(resultCpuLoopsNum);
shortestPathCostNum.Text = Convert.ToString(resultShortestPathCost);
}
}
I've looked at the Invoke() methods, but I don't know how to get the original instance of my Form1 from the threaded method.
I'm invoking my thread like this...
// Set the algorithm method up in it's own thread
Thread thread = new Thread(new ThreadStart(MyAlgorithm));
// Run the algorithm
thread.Start();
How do I get back to the original thread so that I can run the method to update my text boxes with the latest values?
In Windows Forms, you'd either use Control.Invoke/BeginInvoke or use a BackgroundWorker and perform the update in the progress event handler.
In WPF you'd use Dispatcher.Invoke/BeginInvoke.
In C# 5 and .NET 4.5 you'll be able to use async methods which should make a lot of this much simpler...
I've looked at the Invoke() methods, but I don't know how to get the original instance of my Form1 from the threaded method.
If the "threaded method" is just an instance method of the Form, then you've already got the this reference. If it's not, you'll need to provide that information - ideally as an ISynchronizeInvoke to avoid a direct dependency on Windows Forms if you can express the "update" part separately. (That interface is somewhat deprecated these days, in favour of synchronization contexts, but it still works perfectly well.)
Have a look at Control.Invoke():
public void ShowResults()
{
while (true)
{
Thread.Sleep(1000); // don't spam the UI thread
if (this.InvokeRequired)
{
this.Invoke((Action)UpdateGui);
}
else
{
UpdateGui();
}
}
}
private void UpdateGui()
{
loopsNum.Text = Convert.ToString(resultLoopsNum);
nodesVisitedNum.Text = Convert.ToString(resultNodesVisitedNum);
nodesResolvedNum.Text = Convert.ToString(resultNodesResolvedNum);
cpuLoopsNum.Text = Convert.ToString(resultCpuLoopsNum);
shortestPathCostNum.Text = Convert.ToString(resultShortestPathCost);
}
You can use:
myform.Invoke(ShowResults);
There's other options here too:
Alternately use a System.Forms.Timer to call ShowResults periodically. Or another option would be not to use another thread to do the operation; do it in the GUI thread and call Application.DoEvents() from within the operation when you want to let the GUI update.
The first option is nice because it keeps you from accidentally flooding the GUI with Invoke requests, and the second option is nice because it's all on the GUI thread and allows you to have fine-grain control over when things get displayed on the GUI.
I'm working in a c# windows application with vs2010 and a local database.In one of my forms i use a BindingNavigator with a group of textboxes filled by the database and a ReporViewer. I've added a background worker in order to fill the table adapters in case there are a lot of records in the database.
The problem is that the way I'm using the background worker when i debug my app i cannot see any data in the textboxes, otherwise when i run my app it's working fine. I know that this is a case of accessing the UI on a non-UI thread and it is wrong. Is there a another way around it?Thank you in advance.
Here is the code I'm using:
private void Client_Load(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
this.clientTableAdapter.Fill(this.database1DataSet.Client);
this.projectTableAdapter.Fill(this.database1DataSet.Project);
if (InvokeRequired)
{
this.Invoke(new MethodInvoker(this.reportViewer1.RefreshReport));
return;
}
}
Typically, a background worker returns on the same thread, and would actually throw an exception about the non-UI thread. However, this might be eaten in your case. You should be using the RunWorkerCompleted event for items that are to happen after your main work is done, especially when looking to update the UI. And, this should return to the same thread it was called from (UI in your case) as mentioned above.
So, I would move your UI processing code (RefreshReport) into a new method set up for the RunWorkerCompleted.
However, my suggestion would be to take a look at the Task Parallel Library. It ends up making code much cleaner and easier to debug IMO.
Example (rough and may not compile due to the nulls, but you can get the jist :)):
var task = Task.Factory.StartNew(()=>{//Do Async Stuff});
task.ContinueWith((previousTask)=>{//Do your UI Stuff}, null, null,
TaskScheduler.FromCurrentSynchronizationContext);
//The FromCurrentSync makes sure the method returns
//to the same thread (UI in this case) that it started
I know that is not a direct answer, but more of a suggestion towards what I would consider a cleaner, more debuggable approach.
Somewhat new to C# but I have a major problem with getting these things to work because if my background worker is running a long process by using a method from another class, then that class has no access to the background worker in order to update the progress.
For instance:
private void bgWorker_DoWork(object sender DoWorkEventArgs e)
{
bgArgs args = e.Argument as bgArgs;
MyClass objMyClass = new MyClass();
MyClass.MyMethod(strValue, args.Option);
//Do something based on return value of long process.
}
If I try to update bgWorker from the class "MyClass", it cannot "see" bgWorker, it doesn't exist in the context of the class, it's in the UI class because in Visual Studio, that's where you drag it from the toolbox.
The only way I've gotten it to work is to pass the whole UI form to the class, which creates other problems when trying to access that class from anywhere but the main form. From there I just update the progress bar manually via ProgressBar1.PerformStep() as it runs through the loops.
Also, I've already changed the modifier on my progress bar to internal, so it's not that the class doesn't see the progress bar.
I might be able to pass the bgworker by itself to the class through the method, but that just doesn't seem right.
I think your architecture needs revising here. The background worker is for running operations in the background, like out-of-sight-out-of-mind. While it is running, you can accept feedback from it by observing the BackgroundWorker.ProgressChanged event, which will help you increment your progress bar with PerformStep(). However, you shouldn't attempt to alter the BackgroundWorker while it is running. This gets you into Threading issues, which I'm not sure you really want :) You see, the BackgroundWorker uses a different thread to perform its operations when it runs, so changing it while running means you have to access the thread it is performing its work upon. This gets ugly. It is best to just give it a method to execute, let it run, check in on its ProgressChanged, and wait for it to finish.
Assuming I'm understanding your question correctly you probably need to make a method that can access the UI progressBar despite the source thread. The below will do just that saving you from blowing up the application when you try to set the value.
private delegate void UpdateProgressBarCallback(int barValue);
private void UpdateProgressBarHandler(int barValue)
{
if (this.progressBar1.InvokeRequired)
this.BeginInvoke(new UpdateProgressBarCallback(this.UpdateProgressBarHandler), new object[]{ barValue });
else
{
// change your bar
this.progressBar1.Value = barValue;
}
}
[see http://msdn.microsoft.com/en-us/library/ms171728%28VS.80%29.aspx ]
Then you just call UpdateProgressBar(value); (likewise, if you want this to step you can adjust the arguments/way the method operates)
Next you can go about this a few ways: You can make your background worker (since it's already in another class) event driven and then attach progress changes and update the UI; or you can pass a delegate to the thread workers as a reference so it knows where to adjust the UI.
Comment and leave me a direction to go and I'll see if I can help you (and confirm I understand the question).