I'm trying to do a waiting screen for my application coded in c#.
when the user click on start, to prevent possible errors just show a message in the middle, i used backgroundWorker but doesn't work like i want.
i have this:
backgroundWorker1.RunWorkerAsync(formwaiddt.ShowDialog());
the problem is the program waits until i close this dialog.
also i tried other ways like this
Can you link to a good example of using BackgroundWorker without placing it on a form as a component?
and i obtain the same result.
then anyone knows how to show a message in the middle of the screen, also i create a panel but doesn't shows it correctly, to lock the user interactions
thanks.
The simplest case is to use BackgroundWorker to create a method that matches the delegate signature, attach that method to BackgroundWorker`s DoWork event, and then call the RunWorkerAsync() method of BackgroundWorker:
// Set your cursor to busy
Cursor.Current = Cursors.WaitCursor;
BackgroundWorker backgroundWorkerExample = new BackgroundWorker();
backgroundWorkerExample.DoWork += new DoWorkEventHandler(backgroundWorkerExample_DoWork);
backgroundWorkerExample.RunWorkerAsync();
// elsewhere:
void backgroundWorkerExample_DoWork(object sender, DoWorkEventArgs e)
{
// body of the work elided
}
When the background worker thread has finished, it raises the RunWorkerComplete event on the foreground thread, so you can switch your cursor back to the normal state using:
Cursor.Current = Cursors.Default;
Alternatively, you could use the WorkerReportsProgress property to inform BackgroundWorker that the worker procedure will report progress to the foreground thread at regular intervals.
Have you tried wiring up an event on a timer, so that it runs on a separate thread to check if the process has finished?
Related
In some games, there is a splash screen that downloads content from a server. It might give some tips while you're waiting. I'm doing something similar, only that the loading happens really quick, but I want it to wait a few more seconds.
When the user first loads my application, it has a screen with a progress bar. At the moment, it checks if the server is online. If it is, it says "Connected!" However, it immediately fades out my controls. I want it to wait about 5 more seconds so the reader can read it. Then fade the controls out.
private void frmMain_Loaded(object sender, RoutedEventArgs e)
{
// Start background worker
m_bgWorker = new System.ComponentModel.BackgroundWorker();
m_bgWorker.ProgressChanged += M_bgWorker_ProgressChanged;
m_bgWorker.WorkerReportsProgress = true;
m_bgWorker.RunWorkerAsync();
m_bgWorker.ReportProgress(100);
}
private void M_bgWorker_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
progConnect.Value = 0;
if (SystemBO.IsOnline())
{
lblConnection.Content = "Connected!";
}
progConnect.Value = e.ProgressPercentage;
// TODO: Wait 5 seconds here...
// Fade out controls
lblTitle.BeginAnimation(Label.OpacityProperty, doubleAnimation);
progConnect.BeginAnimation(Label.OpacityProperty, doubleAnimation);
lblConnection.BeginAnimation(Label.OpacityProperty, doubleAnimation);
}
Note:
I tried System.Sleep(), but that made no difference. I understand why. The idea is the same though: I want the background worker to sleep for 5 seconds before completing.
Solution:
I added a few more events: DoWork and RunWorkerCompleted.
Then I added this code:
private void M_bgWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
for (int i = 0; i < 100; i++)
{
m_index++;
System.Threading.Thread.Sleep(40);
m_bgWorker.ReportProgress(m_index);
}
System.Threading.Thread.Sleep(3000); // wait 3 seconds to read
}
It worked. The progress bar animates quite smoothly.
You shouldn't do the fade-out in the ProgressChanged handler. That handler really should only be used to update progress information like text and a percentage bar.
Your fade-out logic should be moved to the separate RunWorkedCompleted event handler. Here you can let the thread sleep/await a scheduled task for a while before beginning to fade out the controls. (Using async/await over Thread.Sleep() here has the advantage of not blocking the UI thread while waiting, so the UI still remains responsive while waiting for the time to pass.)
It also ensures that the code is called at the very end and not every time the worker reports any kind of progress. And it's also cleaner because it allows you to handle different termination states by checking the RunWorkedCompletedEventArgs:
If e.Error is set, an exception occurred in the worker thread.
If e.Cancelled is true, the worker thread got cancelled by calling CancelAsync() on the worker. (Only possible if WorkerSupportsCancellation is set to true and only useful if code in the DoWork handler actually checks for the cancellation flag)
Otherwise everything went okay.
Side note:
I am assuming that your code is not a full example, because you have no actual handler assigned to the DoWork event. So right now it would not do anything. And calling ReportProgress() right from the main thread is wrong as well. That method is designed to be called from within the DoWork event handling method in order to let the asynchronous thread report status updates back to the UI, since those events are handled on the main thread.
rather than the last line in frmMain_Loaded:
m_bgWorker.ReportProgress(100);
I would put a for loop with Sleep(), for simulating some extra processing:
for (int i=0; i<=100; i+=10)
{
if (SystemBO.IsOnline())
i = 100;
Sleep(1000);
m_bgWorker.ReportProgress(i);
}
I would like to do the following:
Have a button and a table on a GUI.
When I press the button, a task is started
This task is a while loop, giving me data on each iteration
How can I run this loop and obtain data from each iteration of it in the main GUI table, without blocking the GUI? This is important, because the while stop condition is again a button on the GUI.
I have tried using a BackgroundWorker, but I really cannot figure out how to send back data at every loop iteration (???) I can get back the result at the end, but that is not the target. If I launch the worker in a loop (but not have the loop in the worker), it does not work.
private void ContinuousCoordinateAquisition(object sender, DoWorkEventArgs e)
{
while (continuousPositionAquisitionFlag == true) // while the monitoring is not stopped, get positions
{
// get xyzwpr world coordinates
robotCoordinatesXYZWPRworld XYZWPRworld = robi.getRobotPosition_xyzwpr_world();
Do something........... retuns values I need in GUI
// sleep for defined time
System.Threading.Thread.Sleep(1000); // wait
}
}
the calling would be
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(ContinuousCoordinateAquisition);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(ContinuousCoordinateAquisitionCompleted);
continuousPositionAquisitionFlag is set from a button (stop button).
ContinuousCoordinateAquisitionCompleted here is only done once unfortunately, not every iteration.
You're on the right track. You should use the background worker, but don't wait for the RunWorkerCompletedEventHandler, that happens when everything is done.
Instead, inside your loop periodically call the ReportProgress method on your background worker. That will trigger the ProgressChanged event that you can handle in your GUI thread.
See here: https://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.progresschanged(v=vs.110).aspx
Try setting the BackgroundWorkClass WorkerReportsProgress property and handle the ProgressChanged event, as described by this article:
https://msdn.microsoft.com/en-us/library/cc221403%28v=vs.95%29.aspx
I have a Console app that displays a WinForms Form.
In the Form, the user clicks button 1 and it runs a long subroutine. I want to have a button 2 that can kill the subroutine at any point. However, the UI freezes when I click button 1 until the subroutine has finished. How can I get the UI to not freeze?
Your long-running code is blocking the UI thread, so you can no longer click the second button, nor interact with the UI in any way until the code is finished executing.
You'll need to move your long-running code to a separate thread. There are various (and newer) ways of doing this, but one way is the BackgroundWorker. It's pretty easy to learn, and wraps some nice functionality, like cancelling the thread.
Here's a short WinForms app to demonstrate. You have to explicitly enable the ability for the thread to be cancelled. In this example, the while loop continues indefinitely, but checks every 100ms to see if there's a request for it to be cancelled. When you click the second button, the cancellation request is sent, and the thread ends.
public partial class Form1 : Form
{
private BackgroundWorker bg;
public Form1()
{
InitializeComponent();
bg = new BackgroundWorker
{
WorkerSupportsCancellation = true
};
bg.DoWork += (sender, args) =>
{
while (true)
{
Thread.Sleep(100);
if (bg.CancellationPending)
break;
}
MessageBox.Show("Done!");
};
}
private void button1_Click(object sender, EventArgs e)
{
bg.RunWorkerAsync();
}
private void button2_Click(object sender, EventArgs e)
{
bg.CancelAsync();
}
}
Following up on chouaib's comment, another nice thing about using the BackgroundWorker in a WinForms environment is that you can drop and drop it onto your designer, similar to a Menu, Timer, etc. You can then access its members in the "properties" panel, setting "WorkerSupportsCancellation" to true, subscribing to events, etc.
From your comment:
"is there a way to run this background process and be able to update the main userform? I keep getting the "cross-thread operation not valid control accessed from a thread other than the..." I want to run the long-running background operation, and have it update the main UI with text in a label (like a percentage of its progress)"
If you want to update the UI while the thread is running, you should do that from the ProgressChanged event. First, enable that option and subscribe to the event:
bg.WorkerReportsProgress = true;
bg.ProgressChanged += bg_ProgressChanged;
Then call ReportProgress() when you want to update the UI. You could pass back a percentage complete and some text, for instance:
bg.ReportProgress(50, "Almost there...");
Finally, update the UI from inside the event:
void bg_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
var message = e.UserState.ToString();
var percent = e.ProgressPercentage;
lblStatus.Text = message + " " + percent;
}
You need to make it multithreaded as suggested in the comments. The older way of doing this was manage your own thread. Then along came the background worker (cheap and easy). Now a days you have other options such as the Task Library.
Remember - anything the runs on the UI thread prevents the UI from sending and receiving events until that operation is finished.
Look into the BackgroundWorker component
I am stuck and was hoping someone could help me.
I have made a class/gui with a loading bar set to marquee so that when a task is being carried out i could display it to the user.
In one of my gui classes, in the constructor on the first line i am making a new instance of this class and then doing
LoadingBar bar = new LoadingBar();
Thread thread = new Thread(bar.Show);
thread.Start();
However, even tho the main programme thread is going off doing some more intensive stuff, this gui still seems to freeze, even if i use backround worker.
Is there anything wrong with the approach i have mentioned and if so what do i need to change?
Thanks
You need to reverse your method. The GUI needs to stay in the main thread while the work is done in a "worker thread" (typically a BackGroundWorker). Then the worker reports back to the GUI which then updates.
You'd better do the opposite. Make your intensive work in the thread (or a background worker), and show the wait screen in the main application thread.
You need to use a BackgroundWorker. Drag one onto your form, click backgroundWorker1 and set the WorkerReportsProgress property to True
Then goto the events (via the properties window) and attach handlers for
DoWork, this is where all the work that is represented by the progress bar. You will "report progress" via this and the background worker will make sure ProgressChanged is called on the UI thread.
ProgressChanged, this is where you update the UI based on progress and state data reported to the method
DoWork event looks something like this
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
var userState = new StateClass();
while (working)
{
// TODO: do work here
// update the state surrounding this task via userState
userState.property = "some status";
// report the progress so that backgroundWorker1_ProgressChanged gets called
this.backgroundWorker1.ReportProgress(percentComplete, userState);
}
}
ProgressChanged event looks like this
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// e.UserState contains the state data you passed to ReportProgress,
// you have to cast it to the right type though, since its of type object
var userState = (StateClass)e.UserState;
int progress = e.ProgressPercentage;
// TODO: report progress to the UI with the above variables
}
Now all you have to do is tell the background worker to do work by calling
this.backgroundWorker1.RunWorkerAsync()
I don't think you're doing all your work within the background worker or the GUI wouldn't be freezing. We'll need to see more of your code or you'll need to have another look at background worker examples to see what could be wrong.
EDIT
Add a call to System.Threading.Thread.Sleep(1); after your report progress call
I created a loadingForm with only a progress bar with marquee style. In my mainForm I'm trying to do this:
//before downloading
loadingForm lf = new loadingForm();
lf.Show();
//start downloading
//finishdownloading
lf.Close();
The loadingForm was shown but the progress bar didn't appear, and the form looked like it was crash. after finishing downloading, the loadingForm was closed and my app continued to run normally. In loadingForm I only have:
void loadingForm_Load(object sender, EventArgs e)
{
progressbar1.visible = true;
}
I already set progressbar1 style to marquee in loadingForm.design. How do I fix this? thanks for your help in advance.
You should have a look at using BackgroundWorker Class for the time consuming actions, so that the UI can continue showing the progress while the background worker thread does the work.
How to: Use a Background Worker
BackgroundWorker Basics in C#
C# BackgroundWorker Tutorial
This is most likely because the download and the form with the progress bar are run on the same thread. You could use a BackgroundWorker to perform the download in a different thread than the form.
The UI thread might not have "resources" to redraw the ui, you should use the background worker as mentioned, or process the application messages in the queue. From the Show() method to the Close() method you should be sure to call Application.DoEvents(), so all windows messages will be processed (along with redrawing messages to your application form)