Update progressbar's value from another thread? - c#

My form creates a backgroundworker that every 6 secs checks something. Result is 1-100 and I want to display this.
But if i do it straight forward i get som cross-thread errors so I after some research delegates is the way to do it.
ive created inside the Form1 class:
public delegate void SetProgressbarValueDelegate(int val);
but how do i "connect it" to actually update the progressbar?
Thanks

Assuming your are using WinForms, The BackgroundWorker class raises a ProgressChanged event which will automatically be marshalled back onto your UI thread. You should make updates to your User Interface within your handler for this event.
There are some simple examples of how to use BackgroundWorker within the MSDN documentation.

Related

How resource efficient is Data Binding vs BeginInvoke (for the purpose of GUI manipulation)

I did a search and this is the closest thing to the question I have in mind
How do I refresh visual control properties (TextBlock.text) set inside a loop?
The example included in that URL is exactly like in my situation, except that I am reading in a constant changing stream of data, and want the changes in values to be reflected in the Windows interface.
I am trying to make the program as efficient as possible, so should I use
(INotifyPropertyChanged + Data Binding)
or following would be better?
Application.Current.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Background,
new Action () => Label1.Content = some_content))
Assuming I perform a lot of buffer/checksum operations using system timers.
WPF, like most other UI frameworks, requires that you modify UI controls from the same thread on which they were created. This thread, often referred to as the "UI thread", is usually the application's main thread, i.e. the one where program execution started.
If you happen to subscribe to the SerialPort.DataReceived event, you are facing a problem, because that event might be triggered on any (background) thread:
The DataReceived event is raised on a secondary thread when data is received from the SerialPort object. Because this event is raised on a secondary thread, and not the main thread, attempting to modify some elements in the main thread, such as UI elements, could raise a threading exception. If it is necessary to modify elements in the main Form or Control, post change requests back using Invoke, which will do the work on the proper thread.
Meaning that inside your event handler, you may not directly manipulate any UI controls.
That is where WPF's Dispatcher.BeginInvoke comes in: It helps you to get some code back on the UI thread. You pass it a delegate, and that delegate will be invoked on the UI thread. Therefore you are allowed to manipulate the UI inside that delegate.
Data binding is no real alternative to BeginInvoke: Data binding also manipulates the UI, based on changes to some data object (or vice versa), but it does not automatically switch to the UI thread. This means that if you're using data binding, you must change the data context only if you're on the UI thread... so you need BeginInvoke anyway.
That all being said, if the use of data binding makes your code simpler and easier to understand, by all means use it! But you might still need Dispatcher.BeginInvoke:
private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Application.Current.Dispatcher.BeginInvoke(..., () => dataContext.SomeProperty = serialPort...;
}

C# Winform Multithread sequence

Am relatively new to C# and coding in general. I am trying to write a program that has some logic and that also indicates progress with a progressbar. I am starting a thread in Main() that does all my business logic. It has events that are trigerred at points that I need the progress bar udpated.
The Form object subscribes to the business logic events and has thread safe delegates that are invoked to update the progress bars and text labels.
My problem is that, as the Form is started in the main thread, I have to start the business logic thread before Application.Run(). When the first ProgressUpdate event is trigerred, the Form object still does not exist. I guess a hacky way is to add Thread.Sleep(100) in the second thread, but I don't like that. How do I get around this? Am I on a completely incorrect track? (Am I even making sense?)
Form1 theForm = new Form1();
CreateReport theCreateReport = new CreateReport();
Thread t = new Thread(new ThreadStart(theCreateReport.DoProcess));
t.IsBackground = true;
theForm.Subscribe(theCreateReport);
t.Start();
Application.Run(theForm);
theForm is the form. theCreateReport is where my business logic starts.
You want to use one or more BackgroundWorker objects instead of your business logic thread. This will manage the threading for you as well as giving you a way to provide progress feedback to the main thread.
Maybe you should start your business logic in OnLoad event?
The Form already exists after you invoke the constructor (on the very first line) - it's just not visible yet. So you don't need to worry, everything is initialized when you start the new thread.
In Main, just create the form and Application.Run it. In the Load event of the form, start your thread.
You don't really gain any advantage from doing things the way you're currently doing them. And as you've already found, it creates a timing/sequence problem.
The best way to fix problems is to not have them in the first place.
I would use BackgroundWorker and you can still use your events and delegates with it. This time round you will be wrapping up and firing Background Worker's "ProgressChanged" and "RunWorkerCompleted" events.
And you Form can listen to these events and update ProgressBar accordingly.
BWorker handles switching to GUI Thread and Exception Handling better.
You can initialize BackgroundWorker on Form Load.

C# Progress Bar within a class?

Simply put, what is a good way to send the progress of some process back to a form from within a class outside the scope of the form?
EG: I have a Input object, a filepath is sent into the constructor of this object and it parses the file. I want to show the progress of reading in the lines of this file back to the user who is in a form outside the scope of the currently running method. Ultimately I will be displaying overall progress and per file progress.
Add an event to your class that the form can subscribe to. UpdateProgress or something like that. Your class would then raise the event every so often to let the form display the progress.
The best approach here would be a BackgroundWorker. It has a specal event ReportProgress (that handles the Invoke logic for you).
It is an easy way to do multi-threading. Follow a good example when implementing the RunWorkerCompleted event.

WinForm And Looping

I have a WinForm set up and a process that loops until a button is pressed on the form.
When I try to run my code, the form does not even display. I suspect this is because the code gets stuck in the loop and doesn't get far enough to display the WinForm. How can I get the form to display and the loop to run after that point?
If you're looping because you need to do something with the GUI periodically while waiting for input, I suggest using a Timer control and its Tick event.
If you want to do non-GUI things while waiting, a more traditional timer is better suited to the task,
You should probably run the loop in a background thread. The BackgroundWorker class makes this pretty easy to do.
Don't do that.
Windows Forms (like most modern user interface development toolkits) is an event-driven framework. You should never use a loop that "waits" for something to happen; instead you want to use an event that triggers something to happen.
Essentially what's happening is this: WinForms has a loop running a message pump that listens for events from Windows and triggers C# events in response to them. Your code is executing on the same thread as that message pump (it has to, since in WinForms only one thread is allowed to touch any given control). So if you put that thread into a loop, the WinForms code that should be pumping messages isn't, and your user interface appears to hang, since it isn't responding to any messages from Windows. (If you keep clicking it, you will fill up the message queue and get a dialog box that says "This application has stopped responding, do you want to terminate?" or something like that.)
The correct solution is to do one of the following:
Use a Timer
Use a BackgroundWorker
Use a ThreadPool
Another solution that would work, but is not a good idea is:
Use Application.DoEvents() -- but please don't actually do this
Your form loading is freezing because the UI of a windows form runs in a single thread. And the logic that you put on the Load event of this form is running on that thread.
You can run your loop on a separate thread easily by using a BackgroundWorker component on your windows form. On the event DoWork of your background worker, you place the code that has the loop that should run without block your UI. On the Form.Load event, you can start the background worker component by calling the method RunWorkerAsync. On the event handler of your button, you place a code to stop the background worker by calling CancelAsync method.
The article How to: Implement a Form That Uses a Background Operation shows exactly how to accomplish it.
About your comment on not being able to update the Text of a TextBox from a your background worker component. It happens because it is not allowed to modify the state of a windows forms control from a different thread (your background worker code is running on a separated thread) MSDN documentation says:
Access to Windows Forms controls is not inherently thread safe. If you have two or more threads manipulating the state of a control, it is possible to force the control into an inconsistent state. Other thread-related bugs are possible, such as race conditions and deadlocks. It is important to make sure that access to your controls is performed in a thread-safe way.
A sample of how you can update the state of your windows forms controls from your background thread will be similar to the one below (assuming that the new value is already stored on a String variable named text):
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.textBox1.Text = text;
}
I borrowed this code snipped from How to: Make Thread-Safe Calls to Windows Forms Controls article. It can provide you more information about how to deal with multi-threaded windows forms.
You can use the form load event to trigger the start of the loop.
So it would handle the event Me.Load
However is it necessary for your loop to be happening inside of the UI?
This happens because your loop is keeping the window function from processing messages, such as those that tell it to repaint itself. Place a call to Application.DoEvents() inside of your loop to allow the UI to continue to function.
However, you need to ask yourself why you're looping like this in the first place. If you're, say, copying a bunch of files, this might make sense. For most tasks, though, responding to a timer tick should do the trick and won't block the UI.
You should run your loop in a background thread using the BackgroundWorker component.
Remember that the background thread cannot directly interact with the UI controls.
To report the progress on the UI, you should call the BackgroundWorker's ReportProgress method in the background thread, and handle the ProgressChanged event to update the UI.
You can call the CancelAsync method when the Button is clicked, and loop until the CancellationPending property is true.

Delay loading of combobox when form loads

I've got a Windows Forms (C#) project with multiple comboboxes/listboxes etc that are populated when the form loads.
The problem is that the loading of the comboboxes/listboxes is slow, and since the loading is done when the form is trying to display the entire form isn't shown until all the controls have been populated. This can in some circumstances be 20+ seconds.
Had there been a Form_finished_loaded type of event I could have put my code in there, but I can't find an event that is fired after the form is done drawing the basic controls.
I have one requirement though - the loading has to be done in the main thread (since I get the items from a non-threading friendly COM-application).
I have found one potential solution, but perhaps there is a better way?
I can create a System.Timer.Timer when creating the form, and have the first Tick be called about 1 second later, and then populate the lists from that tick. That gives the form enough time to be displayed before it starts filling the lists.
Does anyone have any other tips on how to delay the loading of the controls?
There is the Shown event that "occurs whenever the form is first displayed.". Also you may want to use the BeginUpdate and EndUpdate functions to make the populating of your combobox faster.
It has that certain smell of workaround, but this approach should fulfil your needs:
private bool _hasInitialized = false;
private void Form1_Shown(object sender, EventArgs e)
{
if (!_hasInitialized)
{
ThreadPool.QueueUserWorkItem(state =>
{
Thread.Sleep(200); // brief sleep to allow the main thread
// to paint the form nicely
this.Invoke((Action)delegate { LoadData(); });
});
}
}
private void LoadData()
{
// do the data loading
_hasInitialized = true;
}
What it does is that it reacts when the form is shown, checks if it has already been initialized before, and if not it spawns a thread that will wait for a brief moment before calling the LoadData method on the main thread. This will allow for the form to get painted properly. The samething could perhaps be achieve by simply calling this.Refresh() but I like the idea of letting the system decide how to do the work.
I would still try to push the data loading onto a worker thread, invoking back on the main thread for populating the UI (if it is at all possible with the COM component).
Can you get your data from a web service that calls the COM component?
That way, you can display empty controls on a Locked form at the start, make Asynchronous calls to get the data, and on return populate the respective combos, and once all of them are loaded, you can unlock the form for the user to use.
You could listen for the VisibleChanged event and the first time it's value is true you put your initialization code.
Isn't FormShown the event you're looking for?
When you say that you cannot use a background thread because of COM what do you mean? I am using many COM components within my apps and running them on background threads.
If you create a new thread as an STAThread you can probably load the ComboBox/ListBox on a Non-UI thread. IIRC the ThreadPool allocates worker threads as MTAThread so you'll need to actually create a thread manually instead of using ThreadPool.QueueUserWorkItem.

Categories

Resources