I have a piece of code which gets the available server instances from my SQL server to DataTable. It will take some time to populate. So I tried to add a progress bar to indicate that time. There are some ways to do that:
1.Using Background worker
2.Using a timer
I used a timer but progress bar is not animating.
my code is(WinForm) :
private void frmCodeGenerator_Load(object sender, EventArgs e)
{
Cursor.Current = Cursors.WaitCursor;
tmrWaiting.Enabled = true;
tmrWaiting.Start();
//List all available servers
PopulateServerInstances();
//List all default namespaces to import
//Set the target folder default path tec.
tmrWaiting.Stop();
Cursor.Current = Cursors.Default;
}
private void tmrWaiting_Tick(object sender, EventArgs e)
{
pbWaiting.Value++;
}
Cursor is changing form normal to waitstate but progress bar didn't. Am I doing wright?
Your Load event handler, which is running on your UI thread, is calling PopulateServerInstances, which is presumably blocking whilst loading the data. You need to do that work on a BG thread so that your UI thread can continue to update the UI. BackgroundWorker is your best bet, because it includes support for updating progress information on the UI thread as you go along.
Related
I want to make a button to perform some action and THEN let user know it was done. I tried making a label ander a button, then pause, then making it disappear.
private void button1_Click(object sender, EventArgs e)
{
// some action
label1.Text = "Done!";
System.Threading.Thread.Sleep(500);
label1.Text = "";
}
But it doesn't work. What is my mistake?
As Grant says in his answer, you're blocking the UI thread. The simplest solution is to spawn a new Task which will do the update for you, thus releasing the UI thread.
The Task can simply use Invoke to push the update back to the UI thread after a Sleep.
In your case, this translates to something like this:
private void button1_Click(object sender, EventArgs e)
{
// some action
label1.Text = "Done!";
new TaskFactory().StartNew(() =>
{
Thread.Sleep(5000);
Invoke((Action)(() => label1.Text = string.Empty));
});
}
The call to Thread.Sleep() freezes the UI thread for a half-second, so that no updates to the UI can happen (including your update to the Label's text).
Here's a couple options:
You could use a BackgroundWorker, which has built-in mechanisms for executing long-running code in a separate thread, and then updating the UI (such as your Label) when it's done.
You could add a Windows.Forms.Timer to your Form, to perform the action and update the Label. Give it an interval of 500 (ms), and it'll wait roughly a half-second before firing.
I have a main UI that doing some time-consuming work. When it is executing, I would like to open a second form with a progress bar (marquee style) to indicate "working on it".
I have seen people putting the time-consuming task in the BackgroundWorker, however, I would like to run in the main UI thread.
The time-consuming task will be executed in MainForm. I would like to reuse the progress bar for various process, so I am writing a second form ProgressBarForm with BackgroundWorker in it, that would start the _mainWork at the same time as showing progress bar, and will stop and close the ProgressBarForm when _mainWork is done.
Because forms are modals, I am thinking of showing ProgressBarForm in the BackgroundWorker in order not to block MainForm.
Please note that I am not running mainForm in BackgroundWorker. My backgroundWorker just show the form and perhaps report a timer.
public partial class ProgressBarFom : UControl
{
public delegate void MainWork();
private MainWork _mainWork;
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//progressBar.Hide();
this.OnClose(sender, e);
//
backgroundWorker.Dispose();
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
//show this ProgressBarForm
this.ShowDialog();
//stop backgroundWorker
//calling this.Close() in RunWorkerComplete
if (backgroundWorker.CancellationPending == true)
{
e.Cancel = true;
return;
}
}
public void CallProgressBar(object sender, EventArgs e)
{
//progressBar.Show();
backgroundWorker.RunWorkerAsync();
_mainWork();
if (backgroundWorker.IsBusy)
backgroundWorker.CancelAsync();
}
}
In MainForm, I am passing mainwork and call ExecWithProgressBar
private void ExecWithProgressBar()
{
ProgressBarFom .MainWork mainWork = new ProgressBarFom .MainWork(ProgressBarMainWork);
ProgressBarFom prBar = new ProgressBarFom (mainWork);
prBar.CallProgressBar(null, null);
}
Some problems I encoutered
Inside DoWork, the same modal issue occurs. ShowDialog() will block the thread and therefore I never get to check CancellationPending to close ProgressBarForm.
ProgressBarForm starts later then the mainWork. I thought when I called CallProgressBar, the backgroundWorker should start well before my mainWork.
Is worker.Dispose() necessary in RunWorkerComplete?
Would it be a better choice to run mainWork in Worker? And why? I decided to let the main thread run this to not disturb the normal flow, what in Main thread will remain in Main thread, Progress bar is like an addon. If we bring it to the worker, would we need another thread to for progress bar itself?
Unless you do some very ugly hacks (like running more than one message loop inside your application) you cannot display a dialog if the thread running the main window is busy. All dialogs use the same thread to do the display update stuff in WinForms. In fact, they even must be running on the same thread.
There's one (sometimes acceptable) hack using Application.DoEvents(), but I wouldn't use it either, because it gets you into a lot of problems as well.
So the simple answer is: This doesn't work. Use a background worker to do lengthy processing.
I have windows form. I have put one loading image in PictureBox
When I load form then I have set
PictureBox1.Visible = false;
While i am firing click event of button I have set
PictureBox1.Visible = true;
But in that event there is some code to retrieve data from database through stored procedure.
When it will jump to code for retrieving data from stored procedure that loading image stops to load.
It should not be happened. It should show as loading. I have used .gif loading image.
How can I solve this issue ?
Everytime you have a long lasting call within an eventHandler you should use a BackgroundWorker! A BackgroundWorker can run code async and therefor your button_click eventHandler will end right after starting the worker.
// add a BackGroundWorker bwLoadData to your form
private void YOURBUTTON_Click(object sender, EventArgs e)
{
PictureBox1.Visible = true;
bwLoadData.RunWorkerAsync();
}
private void bwLoadData_DoWork(object sender, DoWorkEventArgs e)
{
// access your db, execute storedProcedue and store result to
e.Result = YOUR_DATASET_RECORDS_OR_ANYTHING_ELSE;
}
private void bwLoadData_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Result != null)
{
// e.g. show data on form
} else {
// e.g. error message
}
}
Most probably, while you are running the stored procedure, the UI thread is blocked. You may use a BackGroundWorker in order to fetch data from database, which creates another thread and does not block your main thread.
Or you may create a thread manually and use it to retrieve data from database.
In windows forms, as a best practice, it is better to use another thread for running external system calls in order not to block UI thread.
backgroundworker usage
The possible reason for this might be sharing of same thread by loading image and data retrieval. So, you might try with multithreading or async calls to get data . Sorry for the previous answer about ajax / javascipt web worker, I completely neglected u mentioned windows form.
I am attempting to update the progress bar on a main form with the work being done in a different class. For example:
private void transfer_Click(object sender, EventArgs e)
{
Guid aspnetUserId = Guid.Parse(System.Configuration.ConfigurationSettings.AppSettings["ASPNetUserID"]);
WC1toWC2Transfer transfer = new WC1toWC2Transfer(aspnetUserId);
backgroundWorker1.RunWorkerAsync(transfer);
}
And then in the background method actually call the method:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
WC1toWC2Transfer transfer = e.Argument as WC1toWC2Transfer;
transfer.WC1ToWC2EmployerTransfer(log, wc1ConnStr, wc2ConnStr, progressBar1);
}
In the WC1ToWC2EmployerTransfer method, I'm setting the progress bar maximum and updating the value everytime something is transferred to the database in this method, but when I do this nothing happens. There's code inside the method that runs a stored procedure in a database, but as soon as it hits that portion of the code, it stops debugging and I have to run the process again.
Am I doing something wrong here? Do I need to rewrite what I have so the actual methods are in the main form and not in a different class? I'm a junior developer (just started a few months ago) so forgive me if I'm doing something blatantly wrong or if I didn't explain this well enough.
Any help would be greatly appreciated.
You cant alter the UI unless you are on the main thread, which you BackgroundWorker will not be.
What you need to do is create an event handler in the main form to handle the backgroundworker's ProgressChanged event.
eg
// this method should be in your main form.
private void backgroundworker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// update your progress bar here.
}
In your background worker, you call the ReportProgress method which will fire the ProgressChanged event.
There is a nice example here.
I think the reason is that you get an IllegalCrossThreadException, because you're attempting to access the control from a different thread than it was created. The BackgroundWorker provides a ReportProgress method and a ProgressChanged event that is typically used for such updating and which will be executed on the UI thread. When accessing the progressbar from another thread than the UI thread, do it like this:
if(progressBar1.InvokeRequired) {
progressBar1.Invoke(new MethodInvoker(delegate { progressBar1.Maximum = someValue; }));
}
As a sidenote: It's not very good design to pass the progressbar to your worker class. The form could close, it could get disposed and the worker would not know anything about it, eventually failing with an ObjectDisposedException that probably isn't caught. Additionally, you're making the worker dependent on System.Windows.Forms when it probably doesn't need to. Rather let your worker report progress through an event and pass that on to your progressbar from the class that created the worker.
I'm writing a forms application. I'm adding a piece that allows you to double click on a row of a datagridview control to open a new form with more details. These additional details are pulled from a database, which takes a bit of time to finish.
If I run the DB query from the form's load event, the form doesn't show up until everything in the load event has completed, which can take several seconds.
I want the form to show up instantly when you double click, and all of the fields to be populated once the data is ready.
Is there an event I should be using other than Load?
The standard way to accomplish this is to use a background worker thread and disable the button until the worker thread completes. There is a complete event you can subscribe to on the background worker.
You should use threading. Kick off a thread to do the data retrieval in the form's load event. Introduction to threading
You should use a BackgroundWorker to load the data in a background thread without freezing the UI.
If you really want to load on the UI thread, you should handle the Shown event.
This is an c# example using BackgroundWorker as the other posts metioned that loads unit definitions from .xml an file and changes the status label when it finishes. I stuck in the form intializer, but maybe it is better to start it in an OnLoad() override.
public MainForm()
{
InitializeComponent();
BackgroundWorker bw = new BackgroundWorker();
bw.WorkerReportsProgress = false;
bw.WorkerSupportsCancellation = false;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
unitsToolStripLabel.Text = "Loading Units";
bw.RunWorkerAsync();
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
...
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
unitsToolStripLabel.Text = string.Format("{0} Units Loaded", Units.UnitLibrary.WorkingSet.Count);
unitsToolStripLabel.LinkBehavior = LinkBehavior.HoverUnderline;
unitsToolStripLabel.Click += new EventHandler(unitsToolStripLabel_Click);
}
Please explain a little more on why you do not want to use threading/backgroundworker?
Whilst the correct way to do this is the BackgroundWorker thread, a quick and dirty method is to start a timer on the Load event and get the data when the timer expires. Say 10ms is enough for the form to be painted, then you can disable the controls and set the cursor busy while you get the data. But this is still going to lock up the UI thread while the database is busy leading to repainting artifacts eg if part of the window is covered, and doesn't allow you to display progress using a progress bar.
You can let the load event finish, then start another method to pull data from your database. The initialization of the UI can be done after the form has completed loading, but make sure your UI controls are disabled while you're initializing them.