This question already has an answer here:
Control 'progressBar1' accessed from a thread other than the thread it was created on in my business class [duplicate]
(1 answer)
Closed 4 years ago.
I have a function with several loopings and database query, I want to call it asynchronously by passing a progress bar to show the user the progress.
When I call the thread the program hangs I can not even close
when I call synchContext.Post (state => etlBusiness.LoadData (progressBar), null); it freezes, it is not feasible to bring the logic of loadData to UI there are many methods being called the inside
public partial class Home : Form
{
public Home()
{
InitializeComponent();
synchronizationContext = System.Threading.SynchronizationContext.Current;
}
private SynchronizationContext synchronizationContext;
public SynchronizationContext context = SynchronizationContext.Current;
public Thread _myThread = null;
private void btnSend_Click(object sender, EventArgs e)
{
_myThread = new Thread(() => LoadData(synchronizationContext, progressBar1));
_myThread.Start();
}
private void LoadData(System.Threading.SynchronizationContext synchContext, ProgressBar progressBar)
{
string filePath = tbPath.Text;
ETLBusiness etlBusiness = new ETLBusiness(filePath);
synchContext.Post(state => etlBusiness.LoadData(progressBar), null);
_myThread.Abort();
}
}
You don't need to use Thread.Abort(), SynchronizationContext or even use "asynchronous" code (I assume you're referring to await/async which you cannot call unless your target API actually provides true async functionality, note that using Task.Run is not the same thing): WinForms has built-in functionality for running code in the UI thread in the Invoke/BeginInvoke methods.
For progress reporting, I don't recommend passing-around a ProgressBar as that's a brittle design and means your inner business logic has a dependency on WinForms which prevents you from using it in a WPF, ASP.NET or headless process; instead you could have a private method that updates the UI via a callback, like so:
private ProgressBar progressBar;
public Home()
{
this.InitializeComponent();
}
private void btnSend_Click( Object sender, EventArgs e )
{
Task.Run( (Action)this.LoadData )
}
private void UpdateProgress( Float progress )
{
if( this.InvokeRequired )
{
this.BeginInvoke( (Action<Float>)this.UpdateProgress, progress );
return;
}
this.progressBar.Value = progress * this.progressBar.Maximum;
}
private void LoadData()
{
ETLBusiness etlBusiness = new ETLBusiness(filePath);
etlBusiness.LoadData( this.UpdateProgress ); // You'll need to replace its progressBar parameter with a callback to `this.UpdateProgress`.
}
Where your ETLBusiness.LoadData method should be changed to this:
void LoadData( Action<Float> progressCallback );
me again. I posted a comment in your last post. The problem is coming from my solution. What is happening is you are creating and starting a thread, you then, with synchContext.Post() send the logic of ETLBusiness.LoadData() back to the main thread. What needs to happen is one of the two following options:
Move the logic of ETLBusiness.LoadData() into Form.LoadData() (the method called by the thread), and then use synchContext.Post(state => progressBar1.SetPercent()) to update the progressBar specifically.
Move the thread to the ETLBusiness class. and use synchContext.Post(state => progressBar1.SetPercent()) to update the progressBar specifically.
Sorry again, this problem came from my solution to your previous post
Related
This question already has answers here:
How do I update the GUI from another thread?
(47 answers)
Closed 4 years ago.
I am attempting to plot two graphs simultaneously using multi threading, however, the charts return an error message stating "Control 'chart1' accessed from a thread other than the thread it was created on.". I believe this can be resolved using "Invoke", but I am unsure of how this can be done.
Here is simplified code for one graph and one thread:
private void button1_Click(object sender, EventArgs e)
{
Thread th = new Thread(thread);
th.Start();
}
public void graph(List<double> xlist, List<double> ylist)
{
chart1.Series["1"].Points.DataBindXY(xlist, ylist);
}
public void thread()
{
List<double> xlist = new List<double>();
List<double> ylist = new List<double>();
//Assume xlist and ylist have a range of numerical elements
graph(xlist, ylist);
}
Any help would be appreciated.
This is due to the fact that whilst using Windows Forms, the only thread that can update the GUI is the main thread. You can get around this by using an async await pattern, so that calculations are run in the background and by invokeing the methods required to update your GUI.
Here's an example:
private void MyMethod(var myObject)
{
if (form.myControl.InvokeRequired)
{
form.myControl.Invoke((MethodInvoker)delegate {
// Running on the UI thread
form.myControl.MyMethod(myObject);
});
// Back on the worker thread
}
else //Must already be on the UI thread
{
form.myControl.MyMethod(myObject);
}
}
In this method we check to see whether or not the code is running on the main thread with InvokeRequired, if it isn't, we create a MethodInvoker from a delegate and run our change on the main thread using control.Invoke(). If it is already on the main thread, we just make our change.
Also, see these resources:
https://www.dotnetperls.com/async
https://learn.microsoft.com/en-us/dotnet/framework/winforms/controls/how-to-make-thread-safe-calls-to-windows-forms-controls
This is how it's done:
chart1.Invoke(c => c.Series["1"].Points.DataBindXY(xlist, ylist), new object[] { chart1 });
For easier use in multiple places you could write an extension method:
public static class ControlExtensions
{
public static void UpdateOnUIThread<T>(this T control, Action<T> action) where T : ISynchronizeInvoke
{
if (control.InvokeRequired)
control.Invoke(action, new object[] { control });
else
action(control);
}
}
and use it as:
chart1.UpdateOnUIThread(c => c.Series["1"].Points.DataBindXY(xlist, ylist));
I know that this question had been asked 100 times before, but all the answers I read didn't worked for me. So, I'll try my luck and ask again.
I have a SliderBar that calls a method on the ValueChanged event.
In this method I do some stuff that takes time, and I want that in this time the user will see an "working" ProgressBar (IsIndeterminate=true).
Unfortunately, I don't succeed to make the ProgressBar start working (in the UI) until all the method loops finished.
I tried threads, BackgroundWorker and async Tasks but with no success..
What am I doing wrong?
This is the method:
private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e){
WorkingBar.IsIndeterminate = true; //change the progressBar
SqlAThread = new Thread(SqlAStart);
SqlAThread.Start();
}
The thread:
private void SqlAStart()
{
... //do some stuff
}
In Slider_ValueChanged you start a new tread to run the time-consuming SqlAStart() method, but SqlAStart immediately pushes the work back to the UI thread (via Dispatcher.Invoke()). Therefore, your UI hangs and you don't see any ProgressBar progress until the work is done.
In SqlAStart, only do a Dispatcher.Invoke() where you need to update the progress bar:
private void SqlAStart()
{
ServerThread = new Thread(HttpSrv.listen);
ServerThread.Start();
...
this.Dispatcher.Invoke((Action)(() => {
WorkingBar.Value = ...
}));
....
}
This is quite easy using IProgress<T>:
private async void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (State.Value > 0)
{
var progress = new Progress<int?>(value =>
{
WorkingBar.IsIndeterminate = (value == null);
if (value != null)
WorkingBar.Value = value.Value;
});
await Task.Run(() => doWork(progress));
Task.Run(SqlAStart);
}
...
}
private void SqlAStart(IProgress<int?> progress)
{
ServerTask = Task.Run(HttpSrv.listen);
...
}
private void doWork(IProgress<int?> progress)
{
if (progress != null)
progress.Report(null);
...
}
Note that you should (almost) never use Dispatcher, Thread, or BackgroundWorker in modern applications.
Rather than type out the whole lengthy process, I'd invite you to take a look at my answer to the Progress Bar update from Background worker stalling question here on StackOverflow. It demonstrates how to correctly use a BackgroundWorker object to update the progress value of a ProgressBar.
You may also like to view the BackgroundWorker Class page on MSDN for more detailed information.
I have a Windows Form, inside I have a Button and a Panel.
On button click I'm adding a control to the panel... as many as I want. This process is using Task Factory. Example:
private void ButtonClick()
{
// This line needs to happen on the UI thread...
TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() =>
{
CustomControl.PersonResult newControl = new CustomControl.PersonResult();
this.panel1.Controls.Add(newControl);
}, CancellationToken.None, TaskCreationOptions.None, uiScheduler);
}
My PersonResult.cs control has the following layout:
The control has a picturebox and 3 lables.
Now, when a new PersonResult control is added to the form, I want to perform other background worker in order to get an image from the internet and place it in the picturebox.
So, the scenario is:
I press the button many times and immediately I will see the customcontrol added to the panel, but the picturebox of every control will be empty, but then images will start appearing as soon as the worker loads the image from internet and place it on the picturebox.
Any clue on how do implement this?
Thanks a lot
Any time you touch a control you must do so from the UI thread. You can do the actual downloading of the picture and other long-running tasks from your thread/task. From your thread you use Control.Invoke to invoke a delegate on the UI thread.
This MSDN article has a good example of using Control.Invoke with simple variables like strings and such:
http://msdn.microsoft.com/en-us/library/a1hetckb(v=vs.100).aspx
But often a helper class is used so you can pass more complex data between your delegates without resorting to big nasty object arrays.
Here's some sample code I did up:
private void button1_Click(object sender, EventArgs e) {
Task.Factory.StartNew(() =>
{
DataClass dataClass = new DataClass(this.textBox1);
Thread.Sleep(5000); // simulate long running task
dataClass.TextToPass = "set some text";
dataClass.updateTargetControl();
});
}
private class DataClass {
delegate void updateTextBoxOnUiThreadDelegate();
private TextBox _targetControl= null;
private updateTextBoxOnUiThreadDelegate _updateDelegate;
internal DataClass(TextBox targetControl) {
_updateDelegate = new updateTextBoxOnUiThreadDelegate(updateOnUiThread);
_targetControl = targetControl;
}
internal string TextToPass = "";
internal void updateTargetControl() {
_targetControl.Invoke(_updateDelegate);
}
private void updateOnUiThread() {
_targetControl.Text = this.TextToPass;
}
}
Something along the lines:
You don't have to add controls asynchronously. Add them in the GUI thread and every time create a new worker thread supplying it with the delegate from your control which will be called asynchronously (using BeginInvoke?) when the worker finished loading the image.
I am not quite sure I understand why you've wrapped a UI operation in its own Task.. when it isn't chained to an async Task.
Anyway.. PictureBoxes have a LoadAsync method. Why make this harder than it needs to be?
private void ButtonClick()
{
CustomControl.PersonResult newControl = new CustomControl.PersonResult();
this.panel1.Controls.Add(newControl);
newControl.PictureBox.WaitOnLoad = false;
newControl.PictureBox.LoadAsync("http://url-here.com/image.jpg");
}
I've been trying to learn more about asynchronous tasks and threading but not making a ton of headway.
I'm trying to load an "Engine" type of thread that will run in the background upon launch and be able to access the UI Thread to update variables, without hanging the UI Thread.
In the below code, Engine is called, and a Ticker object is created which holds the current value of (Litecoin/USD) called Last, also holds several other values that would be useful. This code successfully assigns the current value to label1.text. I don't necessarily need code but what approach would I take to create a ticker object in the background every second and update the UI thread with each new Ticker objects values.
Is this a good case for a background worker?
private void Form1_Load(object sender, EventArgs e)
{
Engine();
}
private void Engine()
{
Ticker ltcusd = BtceApi.GetTicker(BtcePair.LtcUsd);
label1.Text = "LTC/USD:" + ltcusd.Last;
}
EDIT:
If I do the following, label1 throws an InvalidOperationException due to a Cross-thread operation attempt (label1 in the UI thread).
private void Form1_Load(object sender, EventArgs e)
{
var t = Task.Factory.StartNew(() => Engine());
t.Start();
}
private void Engine()
{
while (true)
{
Thread.Sleep(1000);
Ticker ltcusd = BtceApi.GetTicker(BtcePair.LtcUsd);
label1.Text = "LTC/USD: " + ltcusd.Last;
}
}
Using async/await, the simplest way of getting an "asynchronous" sort of API is to invoke a new task. It's not great, but it'll make things simpler. I would probably create a new class which basically wrapped all the BtceApi methods in tasks:
public class BtceApiAsync
{
public Task<Ticker> GetTickerAsync(BtcePair pair)
{
return Task.Run(() => BtceApi.GetTicker(pair));
}
// etc
}
Then you can use a timer which fires once per second, which will start off a new task and update the UI appropriately:
// Keep a field of type System.Windows.Forms.Timer
timer = new Timer();
timer.Interval = 1000;
timer.Tick += DisplayTicker;
timer.Start();
...
private async void DisplayTicker(object sender, EventArgs e)
{
Ticker ticker = await BtceApiAsync.GetTickerAsync(BtcePair.LtcUsd);
label1.Text = "LTC/USD: " + ltcusd.Last;
}
Note that this doesn't mean the screen will be updated once per second... there will be a new task started once per second, and as soon as each task completes, the UI will be updated.
The use of await here - from an async method started on the UI thread - means you don't need to worry about using the UI; the whole async method will execute on the UI thread, even though the fetch itself happens in a different thread.
You can try ContinueWith to update the Label at the end of the task. If you want to update it event before the task ends then raise an event which is registered by on the UI thread. The event can then update the label.
I suppose this is Windows Forms. You could do it "old school style" and set the label text on the UI thread, and you can do that by passing delegate to the BeginInvoke or Invoke method.
private void Engine()
{
while (true)
{
Thread.Sleep(1000);
Ticker ltcusd = BtceApi.GetTicker(BtcePair.LtcUsd);
UpdateText("LTC/USD: " + ltcusd.Last);
}
}
private void UpdateText(string text)
{
//Inspect if the method is executing on background thread
if (InvokeRequired)
{
//we are on background thread, use BeginInvoke to pass delegate to the UI thread
BeginInvoke(new Action(()=>UpdateText(text)));
}
else
{
//we are on UI thread, it's ok to change UI
label1.Text = text;
}
}
[Windows forms application & .NET 4.0]
I need to execute database access methods that return objects (either list of classes or simple classes).
Also i need to open forms that are responsive while main thread does initialization.
I need to run these on separate threads keeping the User Interface responsive and of course to be able to pass the results back to main thread for UI updates.
I have been reading books regarding the various ways for this.
I understand that my job can be done by:
BackGroundWorker
Thread Class
Task Class
Which one i should dive into ?
Update: using the suggested Task class i am getting errot for cross thread safety using this:
private void BtnCheckClick(object sender, EventArgs e)
{
var itm = Task<JDEItemLotAvailability>.Factory.StartNew(() =>
Dal.GetLotAvailabilityF41021(
txtLot.Text,
cmbMcu.SelectedItem.ToString(),
cmbLocn.SelectedItem.ToString())
);
lblDescriptionValue.Text = itm.Result.Description;
lblItemCodeValue.Text = itm.Result.Code;
lblQuantityValue.Text = itm.Result.AvailableQuantity.ToString();
LotFocus(true);
}
On the above exmaple i am getting the exception in cmbMcu control not the txtLot.
I would use the Task class, it's really easy to synchronize it and it already provides a support for returning objects.
var task = Task.Factory.StartNew(
() => GetDatabaseData(someArguments),
TaskCreationOptions.LongRunning);
// Example method
public DataSet GetDatabaseData(object args) { ... }
this this tells a scheduler to create and begin a new task and gives it a hint that it might be a good idea not to use a thread-pool thread, if the scheduler uses a thread-pool. Anyway you can now decide how do you want to synchronize.
For example to achieve similar behaviour as in Gregor Primar's answer, you can set up a continuation using ContinueWith method as follows,
task.ContinueWith(oldTask => ProcessReturnedData(oldTask.Result));
// Example method
public IEnumerable<SomeEntity> ProcessReturnedData(DataSet data) { ... }
which will schedule calling the ProcessReturnedData method after the task object has done executing. Note that this will be called even if task fails for some reason, so it may not be always a good solution - or you would have to do some checks in the provided delegate.
If you want to do a non-blocking wait on the main thread and use the returned object there, you can simply use the Wait method.
task.Wait(); // Makes current thread wait until the task is comnpleted.
DataSet result = task.Result; // Accessing the result object.
I hade done a lot of projects using Thread, however Task should be more easy to use.
Here is demo how make async operations using Threads.
This is the class that will return data to ui:
public class MyAsyncClass
{
public delegate void NotifyComplete(DataSet data);
public event NotifyComplete NotifyCompleteEvent;
//Starts async thread...
public void Start()
{
System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(DoSomeJob));
t.Start();
}
void DoSomeJob()
{
//just wait 5 sec for nothing special...
System.Threading.Thread.Sleep(5000);
if (NotifyCompleteEvent != null)
{
//TODO: fill your data...
DataSet ds = new System.Data.DataSet();
NotifyCompleteEvent(ds);
}
}
}
And here is ui implementation:
MyAsyncClass myClass = null;
private void button2_Click(object sender, EventArgs e)
{
myClass = new MyAsyncClass();
myClass.NotifyCompleteEvent += new MyAsyncClass.NotifyComplete(myClass_NotifyCompleteEvent);
//here I start the job inside working class...
myClass.Start();
}
//here my class is notified from working class when job is completed...
delegate void myClassDelegate(DataSet data);
void myClass_NotifyCompleteEvent(DataSet data)
{
if (this.InvokeRequired)
{
Delegate d = new myClassDelegate(myClass_NotifyCompleteEvent);
this.Invoke(d, new object[] { data });
}
else
{
//TODO: show your data
MessageBox.Show("Data retrieved!");
}
}