rwheeler23
asked on
How to add a timer behind call to stored precudure using C#?
I have a lengthy stored procedure that is called from my C# program. I have placed a label on the form and want that label to click up one second as long as the stored procedure runs. When it is done, I want to the label to disappear. How do I get this label to behave like a timer? Do I need to use backgrounder worker to accomplish this? Any sample code would be much appreciated.
ASKER
I like your idea of using async/await and/or backgroundworker. I am going to test a few scenarios. If you have any tips or links please forward them along. Please excuse my ignorance, how do I tell if this is WPF or Winforms? I simply start off the project by selecting a project type off a toolkit I was given.
If it's WPF, then you will be designing your layout by writing (usually) XAML. If you don't have a place where you're writing XAML like:
<Grid>
<TextBox ... />
<Button ... />
</Grid>
...then you're probably on Windows Forms. You can also look at the project properties.
<Grid>
<TextBox ... />
<Button ... />
</Grid>
...then you're probably on Windows Forms. You can also look at the project properties.
ASKER
My code is below. I am getting this message about 'e' variable. What must I change?
========================== ========== ========== ========== ========== =
Severity Code Description Project File Line Suppression State
Error CS0136 A local or parameter named 'e' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter GSE.GP.Customizations C:\Projects\GSE.GP.Customi zations\GS E.GP.Custo mizations\ frmJobLink erUtilitie s.cs 134 Active
========================== ========== ========== ========== ===
private void btnJobCostDetail_Click(obj ect sender, EventArgs e)
{
Class1 c = new Class1();
c.StartClock(ref lblTimer);
var backgroundWorker = new BackgroundWorker();
/* Perform mass refresh of job cost detail */
SqlParameter[] sqlParameters = new SqlParameter[0];
backgroundWorker.DoWork += (s, e) =>
{
try
{
string commandText = "rbsBuildJobCostDetail";
DataAccess.ExecuteNonQuery (Controlle r.Instance .Model.GPC ompanyData base, CommandType.StoredProcedur e, commandText, sqlParameters); /* Perform mass refresh of contracts(jobs) */
MessageBox.Show("Job Cost Detail has been refreshed");
}
catch (Exception ex)
{
if (Model.stackTraceWanted)
{
string eMsg = "GPADDINS ERROR : An unexpected error occurred in refreshing job cost detail " + ex.Message;
eMsg += "\n" + ex.StackTrace;
MessageBox.Show(eMsg);
}
MessageBox.Show("An unexpected error occured in refreshing job cost detail: " + ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
};
backgroundWorker.RunWorker Completed += (s, e) =>
{
c.StopClock();
};
backgroundWorker.RunWorker Async();
}
==========================
Severity Code Description Project File Line Suppression State
Error CS0136 A local or parameter named 'e' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter GSE.GP.Customizations C:\Projects\GSE.GP.Customi
==========================
private void btnJobCostDetail_Click(obj
{
Class1 c = new Class1();
c.StartClock(ref lblTimer);
var backgroundWorker = new BackgroundWorker();
/* Perform mass refresh of job cost detail */
SqlParameter[] sqlParameters = new SqlParameter[0];
backgroundWorker.DoWork += (s, e) =>
{
try
{
string commandText = "rbsBuildJobCostDetail";
DataAccess.ExecuteNonQuery
MessageBox.Show("Job Cost Detail has been refreshed");
}
catch (Exception ex)
{
if (Model.stackTraceWanted)
{
string eMsg = "GPADDINS ERROR : An unexpected error occurred in refreshing job cost detail " + ex.Message;
eMsg += "\n" + ex.StackTrace;
MessageBox.Show(eMsg);
}
MessageBox.Show("An unexpected error occured in refreshing job cost detail: " + ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
};
backgroundWorker.RunWorker
{
c.StopClock();
};
backgroundWorker.RunWorker
}
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
What am I missing here? I took a shot at modifying to not use inline methods. My first method correctly displays a count. The modified second version does not display the count.
BGW-DoesWork.txt
BGW-DoesNotWork.txt
BGW-DoesWork.txt
BGW-DoesNotWork.txt
In your second version, you're using the "RunWorkerCompleted" twice. I think you probably meant to do:
backgroundWorker.DoWork += workCompletedJobCostDetail ;
backgroundWorker.DoWork += workCompletedJobCostDetail
ASKER
Here is my final solution. Why do you prefer to not use inline methods? I had found this code on the web and was completely unfamiliar with the structure.
BGW-DoesWorkNow.txt
BGW-DoesWorkNow.txt
I don't like inline methods (lambdas) for event handlers because you can't explicitly unsubscribe them and they mix method definitions inside other methods, which usually means messy code.
When you use inline methods, the compiler creates a singleton static class during compiling and then swaps out the inline methods for those generated class methods. So you're using a shortcut, but without a way to unsubscribe, you have a higher chance of running into a memory leak that could be problematic.
When you use inline methods, the compiler creates a singleton static class during compiling and then swaps out the inline methods for those generated class methods. So you're using a shortcut, but without a way to unsubscribe, you have a higher chance of running into a memory leak that could be problematic.
ASKER
Thank you so much for your solution and valuable insight!
Winforms:
1. Add a Timer object (best to drag it in via the designer - never add a Timer via code unless you understand how Timers are specifically different from other controls). Name it tmrProcedureRuntime.
2. Add a DateTime property to the form class, name it dtProcedureStarted.
3. During form load, set the timer interval to 1000 and add a Tick event handler.
4. Immediately before you start your procedure, set dtProcedureStarted to DateTime.Now, start tmrProcedureRuntime, and make the label visible (you didn't specify the name, so I'll call it lblProcedureRuntime).
5. In the timer Tick handler, use this code:
Open in new window
6. After the procedure ends, make the label invisible again and stop the timer.
On a side note, you should ideally use async/await or a BackgroundWorker to kick off the stored procedure so that the main UI thread isn't held up waiting for the SP to end.
For WPF, it would be a similar process, but you would normally place a string property into your viewmodel and have the timer call a method in that viewmodel to do the work of calculating the desired value and set the string property to it. Then you would use binding to bind the Text/Content property of your label to that string property in your viewmodel.