Progress bar while sql command execute using background worker - c#

I'm trying to create a progress bar wich shown up when the calculate button clicked. And runs until the execute finishes the process. I'm using background worker for the operation. And the progress bar is just a simple marquee so if the process ends i want to torn false the visible property.
here is the initialize
backgroundworker1 = new System.ComponentModel.BackgroundWorker();
backgroundworker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork);
backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted);
so first when the calculate button clicked:
private void buttonCalculate_Click(object sender, EventArgs e)
{
//execute the background worker
this.backgroundworker1.RunWorkerAsync();
while (this.backgroundworker1.IsBusy)
{
progressBar1.Visible=true;
Application.DoEvents();
}
}
Here is the backgroundworker1_DoWork event
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
connection = new MySqlConnection(prop.connectionString);
string query = "Call telephelyi_2013()";
if (this.OpenConnection() == true)
{
MySqlCommand cmd = new MySqlCommand();
cmd = connection.CreateCommand();
cmd.CommandText = query;
cmd.ExecuteNonQuery();
}
}
and finaly when the operation comleeted it shuld be do this
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progressBar1.Visible = false;
MessageBox.Show("Számítás befejzve!");
}
But after all when i run the program and click on the calculate button the progress bar shown up and just moving never ends. And i also see that the computer is calculating for a while and after stops but i never get that end message!
whats wrong?

You need to just start the background worker and then let it go, rather than holding up the main thread by waiting on the worker. Using DoEvents is just a hack that is going to cause more problems than it'll solve unless you're very intimately familiar with what it does, how it works, and when you should use it.
Just have your button click do this:
this.backgroundworker1.RunWorkerAsync();
progressBar1.Visible=true;
And voila.

Related

Animated GIF in Windows Form while executing long process

I have developed a simple windows Application(MDI) in C# which exports the data from SQL to Excel.
I am using ClosedXML to achieve this successfully.
When the process is executed, I want to show a picturebox containing a animated GIF image.
I am a beginner and don't know how to achieve this, the picturebox appears after the process is completed.
I saw lot of posts which says to use backgroundworker or threading which I have never used and finding it hard to implement.
Can I have an step by step example with explanation.
The two functions which I have created which I call before and after I execute the code.
private void Loading_On()
{
Cursor.Current = Cursors.WaitCursor;
pictureBox2.Visible = true;
groupBox1.Enabled = false;
groupBox5.Enabled = false;
groupBox6.Enabled = false;
Cursor.Current = Cursors.Arrow;
}
private void Loading_Off()
{
Cursor.Current = Cursors.Arrow;
pictureBox2.Visible = false;
groupBox1.Enabled = true;
groupBox5.Enabled = true;
groupBox6.Enabled = true;
Cursor.Current = Cursors.WaitCursor;
}
It is not that hard to add a BackgroundWorker
Open your form in the designer
Open the Toolbox (ctrl+alt+X)
Open the category Components
Drag the Backgroundworker on your From
You will end-up with something like this:
You can now switch to the event view on the Properties tab and add the events for DoWork and RunWorkerCompleted
The following code goes in these events, notice how DoWork use the DowWorkEventArgs Argument property to retrieve the value that is supplied in RunWorkerAsync.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// start doing what ever needs to be done
// get the argument from the EventArgs
string comboboxValue = (string) e.Argument; // if Argument isn't string, this breaks
// remember that this is NOT on the UI thread
// do a lot of work here that takes forever
System.Threading.Thread.Sleep(10000);
// afer this the completed event is fired
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// this runs on the UI thread
Loading_Off();
}
Now you only need to start the background job, for example from a button click event to call RunWorkerAsync
private void button1_Click(object sender, EventArgs e)
{
Loading_On();
backgroundWorker1.RunWorkerAsync(comboBox1.SelectedItem); // pass a string here
}
Done! You have successfully added a backgroundworker to your form.
The best way to achieve that is running the animation in a async task, but accordingly some limitations is it possible to do that on windows forms using a Thread Sleep.
eg: In your constructor,
public partial class MainMenu : Form
{
private SplashScreen splash = new SplashScreen();
public MainMenu ()
{
InitializeComponent();
Task.Factory.StartNew(() => {
splash.ShowDialog();
});
Thread.Sleep(2000);
}
It is very important to put the Thread Sleep after have started a new one, don't forget that every action you did on this thread you need ot invoke, for example
void CloseSplash(EventArgs e)
{
Invoke(new MethodInvoker(() =>
{
splash.Close();
}));
}
Now your gif should work!

System.Windows.Forms.Timer And MessageBox

can someone please let me know why the System.Windows.Forms.Timer continues to show multiple message boxes? I thought that it is on GUI thread ... and therefore after the first messagebox the GUI thread should block. But this is not the case
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
int nValue = 0;
void tmr_Tick(object sender, EventArgs e)
{
nValue++;
MessageBox.Show(nValue.ToString());
}
System.Windows.Forms.Timer tmr = new System.Windows.Forms.Timer();
private void btnStartTimer_Click(object sender, EventArgs e)
{
tmr.Interval = 500;
tmr.Enabled = true;
tmr.Tick += new EventHandler(tmr_Tick);
}
}
The MessageBox.Show() method includes (as all modal dialogs do) a message loop that continues to pump window messages.
Window messages are what allow a window to interact with the user (update itself, accept input, etc.), as well as what allows the Forms.Timer class to work.
If you want your Forms.Timer to stop ticking when the dialog is shown, you need to set the timer's Enabled property to false before you show the dialog.
In your Tick event stop the timer and then start again after MessageBox.Show like:
void tmr_Tick(object sender, EventArgs e)
{
tmr.Enabled = false;
nValue++;
MessageBox.Show(nValue.ToString());
tmr.Enabled = true;
}
The reason you are getting repeated MessgeBoxes is because your timer is continuing after showing the first MessageBox.
A message box does not block the GUI-Thread. It's as simple as that. You can interact with the message box, after all :)
Also: The internal workings of the timer are not clear, but I would guess that it runs on another thread and just returns on the GUI-Thread.

Having problems with DispatcherTimer

I am having some problems with using some of Dispatcher Timers on a WPF window.
On a window I would usually use a timer, but this function doesn't seem to be present on WPF forms so I was advised that DispatcherTimer was the equivalent.
So I have 3 of these timers:
The first one every 30 seconds brings the form forward - this one works correctly.
dispatcherTimer1.Tick += new EventHandler(dispatcherTimer1_Tick);
dispatcherTimer1.Interval = TimeSpan.FromSeconds(30);
dispatcherTimer1.Start();
private void dispatcherTimer1_Tick(object sender, EventArgs e)
{
this.Topmost.Equals(true);
this.Activate();
this.BringIntoView();
this.Focus();
this.Topmost.Equals(false);
}
The second one keeps checking every 100 milliseconds to see if IExplorer is running and if so hides the OK button and shows a message on the forms telling the user to close IExplorer - When you run the form if IE is running is will disable the button and show the message, but after you close IE it doesn't change it back.
What could i do to get the timer to constantly run and update the form if IE is opened or closed?
public Process[] aProc = Process.GetProcessesByName("IExplore");
dispatcherTimer2.Tick += new EventHandler(dispatcherTimer2_Tick);
dispatcherTimer2.Interval = TimeSpan.FromMilliseconds(100);
dispatcherTimer2.Start();
private void dispatcherTimer2_Tick(object sender, EventArgs e)
{
if (aProc.Length == 0)
{
richTextBox3.Visibility = System.Windows.Visibility.Hidden;
button1.Visibility = System.Windows.Visibility.Visible;
}
else
{
button1.Visibility = System.Windows.Visibility.Hidden;
richTextBox3.Visibility = System.Windows.Visibility.Visible;
}
}
And Thirdly, like the second timer runs every 100 milliseconds, once they have click on the OK button, I want to to kill the IExplorer process in the event that the user tries to invoke it, but again like the second timer is doesn't seem to be running constantly.
Any ideas?
dispatcherTimer3.Tick += new EventHandler(dispatcherTimer3_Tick);
dispatcherTimer3.Interval = TimeSpan.FromMilliseconds(100);
dispatcherTimer3.Start();
private void dispatcherTimer3_Tick(object sender, EventArgs e)
{
Process[] Processes = Process.GetProcessesByName("IExplore");
foreach (Process Proc1 in Processes)
{
Proc1.Kill();
}
}
if IE is running is will disable the button and show the message, but after you close IE it doesn't change it back. This is happening because you are not getting process in timer tick event. So change you code as shown below.
dispatcherTimer2.Tick += new EventHandler(dispatcherTimer2_Tick);
dispatcherTimer2.Interval = TimeSpan.FromMilliseconds(100);
dispatcherTimer2.Start();
private void dispatcherTimer2_Tick(object sender, EventArgs e)
{
Process[] aProc = Process.GetProcessesByName("IExplore");
if (aProc.Length == 0)
{
richTextBox3.Visibility = System.Windows.Visibility.Hidden;
button1.Visibility = System.Windows.Visibility.Visible;
}
else
{
button1.Visibility = System.Windows.Visibility.Hidden;
richTextBox3.Visibility = System.Windows.Visibility.Visible;
}
}
In the code snippet, you only get the list of the processes once and then check the same array every time. If it is the same in your real code, make sure you update the process list with each tick.

Updating the UI right before running the BackgroundWorker

I have this really little problem, but which can't be easily solved. Currently, my program has 2 buttons, a "Start" and a "Cancel". When the user clicks the start, the buttons should go instantly:
StartButton.IsEnabled = false;
CancelButton.IsEnabled = true;
But this occurs only when the BackgroundWorker has finished (all the code which will be ran after pressing the button), because the UI is always updated as last. There's no way I could add these commands to the "ProgressChanged" or "Completed" event of the backgroundworker. These events can take up to 10min to complete.
One easy way is to add these commands to the "ProgressChanged" part, and in the end "Complete" change their state again. But I'd like to avoid this, as the buttons should be showing their real state all the time, not after few "ProgressChanged" events. Of course there's always ways around, like not using the button's UI properties.
Is there any short solution for this?
It doesn't work to add the Button.Property changes to the ClickEvent. That's the main problem in this. I can easily use the "Completed" part of BGW to change the Button's back to match the starting state. The problem is to get them set right before all the events and BGW.
if you have a start button like:
this.StartButton = new System.Windows.Forms.Button();
then you can do
this.StartButton.Click += new System.EventHandler(this.button1_Click);
and then do
private void button1_Click(object sender, EventArgs e)
{
StartButton.IsEnabled = false;
CancelButton.IsEnabled = true;
Thread bg = new Thread(new ThreadStart( UpdateDatabase()));
bg.Start();
}
if you want the bg thread to send messages to the UI use the Invoke method like here
public delegate void UpdateUIHndler();
public void UpdateUI()
{
}
and do
if (InvokeRequired)
{
Invoke(new UpdateUIHndler(UpdateUI));
}
Take a look at a previous question of mine (quite similer). I should go for option 1.
ASP.NET Application log while code is running / progress bar
UI will only be delayed 5 seconds. Instead of text update the button styling using AJAX.
you can disable the start button in the click event of that button itself and enable it again it on RunWorkerCompleted event of BGW as shown below
BackgroundWorker _worker = new BackgroundWorker();
_worker.DoWork += new DoWorkEventHandler(_worker_DoWork);
private void StartButton_Click(object sender, RoutedEventArgs e)
{
startButton.IsEnabled = false;
}
void _worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
stratButton.IsEnabled = true;
}
void _worker_DoWork(object sender, DoWorkEventArgs e)
{
//Your processing code
}

Cancelling a BackgroundWorker that performs a DB call

I have a WPF application with a child window.Within that I'm doing a database connectivity check on a button click(in a background worker). The process take some time to complete and within that time if the user closes the child window by clicking on close button, the window closes but the background worker continues to run and displays the message after sometime.
Here is the sample code:
BackgroundWorker worker;
private void Button_Click(object sender, RoutedEventArgs e)
{
worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true;
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
bsyInd.IsBusy = true;
worker.RunWorkerAsync();
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
try
{
if (worker.CancellationPending)
{
e.Cancel = true;
return;
}
// checking database connectivity
string connstring=myconnstring;
SqlConnection con=new SqlConnection(connstring);
con.Open();
if(con.State==ConnectionState.Open)
e.Result=true;
}
catch (Exception)
{
e.Result=false;
}
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
bool canConnect=(bool)e.Result;
if(canConnect)
MessageBox.Show("Connected");
else
MessageBox.Show("Failed");
bsyInd.IsBusy = false;
}
//close child window
private void ChildWindow_CloseButton_Click(object sender, RoutedEventArgs e)
{
//cancel the running process
worker.CancelAsync();
}
All the solutions I found online shows examples that continuosly monitor/poll the CancellationPending property of the background worker within a loop inside Do_Work. Here as the process i intend to do doesn't require any loops, how can I monitor the CancellationPending status on child window close button click event and cancel the background process?
Thanks in advance.
Provided that there is way to abort a blocking call to SqlConnection.Open, you could simply declare the SqlConnection object as a member variable in your class and execute the aborting call in the Close button handler.
private SqlConnection con;
private void ChildWindow_CloseButton_Click(object sender, RoutedEventArgs e)
{
if (con != null)
{
con.Dispose(); // or whatever would abort Open()
con = null;
}
}
In the worker thread Open would presumably throw kind of an Aborted exception, that you could catch and then set e.Cancel = true;.
Dennis, you could use an approach with AutoResetEvent, see AutoResetEvent Class
You signal to the thread on which the sql connection is happening that the resource can be released.
You are still using a separate thread so nothing should be blocked, but with the ability now to communicate between the threads and specially notify one of them to release the resources. Apologies if this is not in the format intended.
That link has an example of how to use the AutoResetEvent.

Categories