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.
Related
I'm creating a Excel VSTO using c#. My operation is easy, just right-click on the cell and click on "Update" and a winform that shows progress status will prompt out and launch the controls on the form is tied to a User Process Controller.
The problem is now that the process has launched and executed before the form is fully load, is there a way that I can block the user process controller from executing before all the control on the progress status form is fully shown and loaded? The image below depict my condition.
I have tried to put my User Process Controller call in Form Activated, Shown, Loaded, and nothing works.
This is the first stage the form loaded. Note : The two line of text has shown that the user control process has been executed.
This is the second stage the form loaded.
This is third stage
And finally it is fully loaded.
I have discovered a "hack" to overcome this issue. I add in a backgroundworker and done the follow code on the Form Constructor.
public SummaryStatus()
{
InitializeComponent();
backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += backgroundWorker_DoWork;
backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
backgroundWorker.RunWorkerAsync();
}
And on the DoWork event of BackgroundWorker
void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
System.Threading.Thread.Sleep(2000);
}
and finally, I added the following code in Run Worker Completed
void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
UpdateUPC upc = new UpdateUPC();
upc.txtUpdateSummary = txtUpdateProgress;
upc.updateProgressBar = UpdateProgressBar;
upc.UpdateStatusOnItem();
}
I understand the above might not be a acceptable solution but it might just provide a workaround for the issue. However, if anyone who has a better solution, do feel free to drop in your suggestion.
I've put a loading message like this on my form :
public void myFunc()
{
lbl_status.Text = "Loading ... Please Wait";
// Some Database Works
lbl_status.Text = "Done";
}
but there is a problem. Some times when I click on the button ( Which does myFunc method ) my application doesn't show the Loading message. It just does the database work than it will show Done message.
I know that sometimes the database work is very fast so Loading message won't show but sometimes it is not that fast, like the fist time I open my app. At that time my application seems to be disabled and no buttons and no textBoxes and ... works and after the database work it will be OK and show Done message and never shows Loading message again!
You can do your db work in background thread:
public void myFunc()
{
lbl_status.Text = "Loading ... Please Wait";
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += bw_DoWork;
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
bw.RunWorkerAsync();
}
EDIT: oops DB works should be in the DoWork event handler :)
void bw_DoWork(object sender, DoWorkEventArgs e)
{
// Some Database Works
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
lbl_status.Text = "Done";
}
Your application, like all Windows GUI application, needs to process the GUI events. It is these events that do things like refresh the labels and make your application seem 'responsive'. See Window Messages. Your winforms application runs the message loop when you call Application.Run:
Begins running a standard application message loop on the current thread
If you block the processing while waiting for database work then it will stop refreshing and it will be unresponsive (not respond to clicks or keyboard). So you have to do your database w/o blocking the main loop. There are several options:
use a BackgroundWorker.
use ThreadPool.QueueUserWorkItem.
use the async database methods like SqlCommand.BeginExecuteReader and completion callbacks
use await methods like SqlCommand.ExecuteReaderAsync
Each method has pros and cons, the easiest to start with is probably the first one. Be aware that from a background thread, or from a completion callback, you must use the Control.Invoke when interacting with the main GUI (ie. when updating the form or any element on it).
Any updates to UI objects must be done on the UI thread.
You should look into Control.Invoke to provide you a way to put your call on the Display thread the Control is on.
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.invoke%28v=vs.110%29.aspx
for further clarification, this also means that your heavy updates should be done on a background thread.
WorkerThread may be a good solution for you.
There is a dataTable which used to filled on click of a button say 'Search'. On this click when dataTable filled with data. I am creating a new thread which updates at dataTable data. But User again clicked on search button. Now that dataTable again filled with New data while a thread was already updating that dataTable. On Search click the code again creates a new thread and starts updating that changed dataTable. Here I am having error as: Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on. Please suggest a solution. Thanks
Check if InvokeRequired on the control you're updating and then do Invoke or BeginInvoke if necessary
Disable button and/or disable new update if update is already in progress
For updating DataGridView's DataSource:
if(DGV.InvokeRequired)
DGV.Invoke(new EventHandler(delegate
{
DGV.DataSource= currentDataTable;
}));
else DGV.DataSource= currentDataTable;
If you're updating from another thread and setting currentDataTable there, maybe you shouold consider creating an event that'll fire and update your GUI when your update is done (or work completed - if you're using BackgroundWOrker) - the Invoke won't be necessary then.
you can assign the search button to
btn.Enabled = false;
when clicking on it and invoke a method the will reEnable it when the search is over
if you don't want the button to be disabled you can use this code
private BackgroundWorker trd;
private void button1_Click(object sender, EventArgs e)
{
if (trd != null)
{
trd.CancelAsync();
}
trd = new BackgroundWorker();
trd.DoWork += new DoWorkEventHandler(trd_DoWork);
trd.WorkerSupportsCancellation = true;
trd.RunWorkerAsync();
}
public void trd_DoWork(object sender, DoWorkEventArgs e)
{
// Your search code
}
that way you abort the thread and restart it. the search is stopped at the middle and won't edit the values with another background worker. the background worker is editing the values meaning there wont be cross threading, as you know no other thread but the UI thread can edit the UI
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.
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.