[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
?
Solved

Digital clock on a dialog using threading

Posted on 2012-09-20
23
Medium Priority
?
741 Views
Last Modified: 2012-09-21
I have a dialog box that has a text box on it I use as a status area for telling the end user what stored procedure just finished.
The problem is that each stored procedure takes about 20 to 40 minutes.
I thaugh it would be nice for the end user to see a digital clock on the form so they could tell the program is responsive and a way to see how long before the stored procedure will be done.

I have digital clock code which is added to the tick event..
Problem is that the form is not updated becuase of the stored procedures running.
I don't know how to us threading for this but I am pretty sure that I need it.

Just need someone to give me some code I can add to my dialog box to make the clock be active on the form while it is also running the stored procedures.
0
Comment
Question by:ConnexusDave
  • 12
  • 11
23 Comments
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 38418300
"I don't know how to us threading for this but I am pretty sure that I need it. "

Add a BackgroundWorker() control from the ToolBox to your form.  Run your stored procedures from the DoWork() handler, which runs in a different thread.  The RunWorkerCompleted() event will fire when the task in DoWork() is complete.  To start the BackgroundWorker(), use the RunWorkerAsync() method:
Public Class Form1

    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
        BackgroundWorker1.RunWorkerAsync()
    End Sub

    Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        ' ... run stored procedures from here ...
    End Sub

    Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
        ' ... this fires when DoWork() is complete! ...
    End Sub

End Class

Open in new window

0
 

Author Comment

by:ConnexusDave
ID: 38418828
when I run the way you suggest there is no stops so it tries to run all of them at once.
I need them to run 1 at a time and a runs and b waits for a to get done them b launches but c waits until b is done them c runs buy d waits until c is done..
All while these are running I get an error when I try to update the listbox with the status messages I have for the end user.. so I think this might be a little more complicated then I first thaugh.
0
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 38418870
Are you using more than one BackgroundWorker()?...just use one and launch the procedures one after the other as before.

For updates, set the WorkerReportsProgress() property to True, then use the ReportProgress() which will cause the ProgressChanged() event to fire.  For the ProgressChanged() event it is safe to update the GUI.

Yes...threading is always more complicated than people think.  ;)
0
Get quick recovery of individual SharePoint items

Free tool – Veeam Explorer for Microsoft SharePoint, enables fast, easy restores of SharePoint sites, documents, libraries and lists — all with no agents to manage and no additional licenses to buy.

 

Author Comment

by:ConnexusDave
ID: 38419038
Private Sub BackgroundWorker1_ProgressChanged(sender As System.Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
        Me.Refresh()
    End Sub

I did this for my gui refresh but as you can see I don't know what I am doing.
0
 

Author Comment

by:ConnexusDave
ID: 38419106
I did take the store procedure code from a sub and placed the whole thing into the
BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork

This was orginally a class that you passed the name of the stored procedure to it then it would run it so I took the code out and hard coded the name of the stored procedure.

What it looks like happens is it run and then closed the dialog that I am trying to run all of this from so it looks like the 20 minute stored procedure is done in 1 second.
0
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 38419117
"I get an error when I try to update the listbox with the status messages I have for the end user"

So, from the DoWork() handler, you call ReportProgress().  You can pass a message (or anything really) using the second parameter.  That message can then be retrieved in ProgressChanged() using "e.UserState" (cast it if necessary when passing more complicated objects):
    Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        ' ... stuff ...
        ' ... stuff ...
        ' ... stuff ...
        BackgroundWorker1.ReportProgress(25, "Patience..")
        ' ... stuff ...
        ' ... stuff ...
        ' ... stuff ...
        BackgroundWorker1.ReportProgress(50, "Almost Done...")
        ' ... stuff ...
        ' ... stuff ...
        ' ... stuff ...
    End Sub

   Private Sub BackgroundWorker1_ProgressChanged(sender As System.Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
        ProgressBar1.Value = e.ProgressPercentage
        ListBox1.Items.Add(e.UserState)
    End Sub

Open in new window

0
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 38419119
"What it looks like happens is it run and then closed the dialog that I am trying to run all of this from"

How/when are you closing the dialog?
0
 

Author Comment

by:ConnexusDave
ID: 38419226
Currently I am not closeing the dialog so it should just hang there until I stop visual studio.
I do now get a status bar update message saying that it is starting but my digital clock doesn't display nor does the time move during timer.tick to show the clock moving and the program doing something.


it also appears that nothing is happening with each timer1.tick action..
0
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 38419245
Well...you haven't shown any of the clock code at all.  Can we see that?
0
 

Author Comment

by:ConnexusDave
ID: 38419261
This is what is in the start button click event.

Timer1 = New System.Windows.Forms.Timer
        AddHandler Timer1.Tick, AddressOf Timer1_Tick
        Timer1.Enabled = True
        Timer1.Start()
       
        BackgroundWorker1.RunWorkerAsync()

        Timer1.Stop()
'======================================================

Private Sub Timer1_Tick(sender As System.Object, e As System.EventArgs) Handles Timer1.Tick
        ToolStripStatusLabel1.Text = FormatDateTime(Now, DateFormat.LongDate)
        Me.Refresh()
    End Sub
0
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 38419280
Remove the Timer1.Stop() call.  Instead, you want to stop the Timer when the background thread is done, inside the RunWorkerCompleted() event.

Also, you shouldn't be using both AddHandler() and "Handles Timer1.Tick".  Pick one or the other.  Since you have a Handles clause, you must have declared Timer1 as "WithEvents".  I'd just get rid of the AddHandler() call.

Finally, you don't need the "Me.Refresh()" call either...  =)
0
 

Author Comment

by:ConnexusDave
ID: 38419610
ok did I tell ya that I call a dialog form and then I use a listbox as a place that I can add status messages to..  Now what happes is the dialog box opens like expected.. no status messages which I had working preThreading and no clock/timer working.. I can now move other windows on top of this dialog and remove them and it autopaints.. which is 1 thing good but the lack of digital clock still bugs me..
0
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 38419635
I understand your frustration.  You're only giving me tiny bits of code at a time, though, so I can't figure out where the problem is.

I need to see more complete code so I can have CONTEXT of what I'm seeing and also determine how your main form and dialog form relate to each other and communicate.
0
 

Author Comment

by:ConnexusDave
ID: 38419651
so I now change the button click to this code and I get the error at the bottom..


runitem = "this will take aprx 25 minutes!!! "
        ListBox1.Items.Add(runitem)
        runitem = "your start time is :" & Now()
        ListBox1.Items.Add(runitem)
        runitem = "Your end time is : " & DateAdd(DateInterval.Minute, 20, Now())
        ListBox1.Items.Add(runitem)
        runitem1 = "===================================================================="
        ListBox1.Items.Add(runitem1)
        SPname = "MakeAccountHistoryTable"
        ListBox1.Items.Add(SPname)
        BackgroundWorker1.RunWorkerAsync()
        SPname = "MakeAccountHistoryTable1"
        ListBox1.Items.Add(SPname)
        BackgroundWorker1.RunWorkerAsync()
        runitem = "MakeAccountHistoryTable2"
        ListBox1.Items.Add(SPname)
        BackgroundWorker1.RunWorkerAsync()
        runitem = "MakeAccountHistoryTable3"
        ListBox1.Items.Add(SPname)
        BackgroundWorker1.RunWorkerAsync()
*******************************************************************
My thaugh on the code above was that before run the worker I would update the listbox to tell the user what is going on but then I get error below..
********************************************************************
This BackgroundWorker is currently busy and cannot run multiple tasks concurrently.
0
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 38419713
If you've already started the BackgroundWorker() with RunWorkerAsync(), then you can't "start" it again if it hasn't completed it's previous tasks.

Just call RunWorkerAsync() ONCE from the Button handler.

Also, you can pass a String directly to your ListBox, thus you can change these TWO lines:

        runitem = "this will take aprx 25 minutes!!! "
        ListBox1.Items.Add(runitem)

into ONE line:

        ListBox1.Items.Add("this will take aprx 25 minutes!!! ")

So much easier to read...

Do you want the those "MakeAccountHistoryTableXXX" entries to occur AS THEY ARE CREATED in the BackgroundWorker()?  If so, see my previous comment on how to use ReportProgress():
http://www.experts-exchange.com/Microsoft/Development/Q_27872031.html#a38419117
0
 

Author Comment

by:ConnexusDave
ID: 38419733
Here is a screen shot of what the dialog box looks like when I run the program.
Notice that at the bottom it is supposed to have the time on the left side but it is not there.
0
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 38419759
You forgot the screenshot bud...   =)

Can you also show me how you are opening the dialog box?
0
 

Author Comment

by:ConnexusDave
ID: 38419808
I attached it honestly... let me try again...
threadingPic.doc
0
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 38419829
OK...

"so I now change the button click to this code and I get the error at the bottom"

I'm not seeing a BUTTON on this form...

Like I said, I need to determine the relationship between all these bits so of code so I can HELP YOU solve the problem.  =)

Obviously there are at least two forms here.  One supposedly with a button.  I have no idea how that dialog is being displayed.  I'm not sure which form has the actual BackgroundWorker() control on it. etc....

If you want help I really need COMPLETE code from at least the two forms involved so I get the "big picture" of what is happening.
0
 

Author Comment

by:ConnexusDave
ID: 38419865
I have a button on a main form that says "Make account history copy" which all it does is open up the dialog that I attached with the screen shot..

On the dialog there is a button that says "start" which I hid once it is pressed so the end user knows that they have pressed it.

Everything else is just like we talked about..

StartcopyBTN startcopyBTN.click event has
StartCopyBTN.Visible = False
        'Timer1 = New System.Windows.Forms.Timer
        'AddHandler Timer1.Tick, AddressOf Timer1_Tick
        Timer1.Enabled = True
        Timer1.Start()

        runitem = "this will take aprx 25 minutes!!! "
        ListBox1.Items.Add(runitem)
        runitem = "your start time is :" & Now()
        ListBox1.Items.Add(runitem)
        runitem = "Your end time is : " & DateAdd(DateInterval.Minute, 20, Now())
        ListBox1.Items.Add(runitem)
        runitem1 = "===================================================================="
        ListBox1.Items.Add(runitem1)
        SPname = "MakeAccountHistoryTable"
        ListBox1.Items.Add(SPname)
        BackgroundWorker1.RunWorkerAsync()

*********************************************************************

Private Sub Timer1_Tick(sender As System.Object, e As System.EventArgs) Handles Timer1.Tick
        ToolStripStatusLabel1.Text = FormatDateTime(Now, DateFormat.LongDate)

    End Sub

Private Sub BackgroundWorker1_DoWork _
                (sender As System.Object, _
                 e As System.ComponentModel.DoWorkEventArgs) _
             Handles BackgroundWorker1.DoWork
        'runitem = "MakeAccountHistoryTable"
        'MonthlyFeeJob.runaprocedure1(runitem)
        BackgroundWorker1.ReportProgress(1, "Started " & SPname)
        'MsgBox("Store procedure " & name & " Starting")
        Try


            Dim sqlConnection1 As New SqlConnection(My.Settings.eomConnstring)
            Dim cmd As New SqlCommand
            Dim myParm As SqlParameter = cmd.Parameters.Add("@RowCount", SqlDbType.Int)
            myParm.Direction = ParameterDirection.ReturnValue
            Dim reader As SqlDataReader
            cmd.CommandText = "MakeAccountHistoryTable"
            cmd.CommandTimeout = 0
            BackgroundWorker1.ReportProgress(25, "Calling Stored Procedure " & SPname)
            percenter = 10
            cmd.CommandType = CommandType.StoredProcedure
            cmd.Connection = sqlConnection1
            sqlConnection1.Open()
            reader = cmd.ExecuteReader()
            sqlConnection1.Close()
            If myParm.Value < 1 Then
                MsgBox("Store procedure " & name & " Returned no records")

            End If
            BackgroundWorker1.ReportProgress(100, "Finished Running the stored procedure MakeAccountHistoryTable")
        Catch se As SqlException
            logger.ErrorException("Stored Stuff sql error query name is  " & name, se)
            MsgBox(se.InnerException.Message)

        Catch E1 As Exception
            logger.ErrorException("Stored Stuff query name is  " & name, E1)
            MsgBox("I am deleting the out table this is where the error is.")
            MsgBox(E1.Message)

        End Try
        '***********************************************************************************************************

        Try

             Dim sqlConnection1 As New SqlConnection(My.Settings.eomConnstring)
            Dim cmd As New SqlCommand
            Dim myParm As SqlParameter = cmd.Parameters.Add("@RowCount", SqlDbType.Int)
            myParm.Direction = ParameterDirection.ReturnValue
            Dim reader As SqlDataReader
            cmd.CommandText = "MakeAccountHistoryTable1"
            cmd.CommandTimeout = 0
            BackgroundWorker1.ReportProgress(50, "Calling Stored Procedure " & SPname)
            percenter = 10
            cmd.CommandType = CommandType.StoredProcedure
            cmd.Connection = sqlConnection1
            sqlConnection1.Open()
            reader = cmd.ExecuteReader()
            sqlConnection1.Close()
            If myParm.Value < 1 Then
                MsgBox("Store procedure " & Name & " Returned no records")

            End If
            BackgroundWorker1.ReportProgress(100, "Finished Running the stored procedure MakeAccountHistoryTable1")
        Catch se As SqlException
            logger.ErrorException("Stored Stuff sql error query name is  " & Name, se)
            MsgBox(se.InnerException.Message)

        Catch E1 As Exception
            logger.ErrorException("Stored Stuff query name is  " & Name, E1)
            MsgBox("I am deleting the out table this is where the error is.")
            MsgBox(E1.Message)

        End Try
        '*********************************************************************************************************************************
        Try

            Dim sqlConnection1 As New SqlConnection(My.Settings.eomConnstring)
            Dim cmd As New SqlCommand
            Dim myParm As SqlParameter = cmd.Parameters.Add("@RowCount", SqlDbType.Int)
            myParm.Direction = ParameterDirection.ReturnValue
            Dim reader As SqlDataReader
            cmd.CommandText = "MakeAccountHistoryTable2"
            cmd.CommandTimeout = 0
            BackgroundWorker1.ReportProgress(75, "Calling Stored Procedure " & SPname)
            percenter = 10
            cmd.CommandType = CommandType.StoredProcedure
            cmd.Connection = sqlConnection1
            sqlConnection1.Open()
            reader = cmd.ExecuteReader()
            sqlConnection1.Close()
            If myParm.Value < 1 Then
                MsgBox("Store procedure " & Name & " Returned no records")

            End If
            BackgroundWorker1.ReportProgress(100, "Finished Running the stored procedure MakeAccountHistoryTable2")
        Catch se As SqlException
            logger.ErrorException("Stored Stuff sql error query name is  " & Name, se)
            MsgBox(se.InnerException.Message)

        Catch E1 As Exception
            logger.ErrorException("Stored Stuff query name is  " & Name, E1)
            MsgBox("I am deleting the out table this is where the error is.")
            MsgBox(E1.Message)

        End Try
        '******************************************************************************************************************************************
        Try

            Dim sqlConnection1 As New SqlConnection(My.Settings.eomConnstring)
            Dim cmd As New SqlCommand
            Dim myParm As SqlParameter = cmd.Parameters.Add("@RowCount", SqlDbType.Int)
            myParm.Direction = ParameterDirection.ReturnValue
            Dim reader As SqlDataReader
            cmd.CommandText = "MakeAccountHistoryTable3"
            cmd.CommandTimeout = 0
            BackgroundWorker1.ReportProgress(100, "Calling Stored Procedure " & SPname)
            percenter = 10
            cmd.CommandType = CommandType.StoredProcedure
            cmd.Connection = sqlConnection1
            sqlConnection1.Open()
            reader = cmd.ExecuteReader()
            sqlConnection1.Close()
            If myParm.Value < 1 Then
                MsgBox("Store procedure " & Name & " Returned no records")

            End If
            BackgroundWorker1.ReportProgress(100, "Finished Running the stored procedure MakeAccountHistoryTable3")
        Catch se As SqlException
            logger.ErrorException("Stored Stuff sql error query name is  " & Name, se)
            MsgBox(se.InnerException.Message)

        Catch E1 As Exception
            logger.ErrorException("Stored Stuff query name is  " & Name, E1)
            MsgBox("I am deleting the out table this is where the error is.")
            MsgBox(E1.Message)

        End Try
    End Sub

 Private Sub BackgroundWorker1_ProgressChanged(sender As System.Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged

        ToolStripStatusLabel1.Text = e.UserState
        ToolStripProgressBar1.Value = e.ProgressPercentage



    End Sub

Private Sub BackgroundWorker1_RunWorkerCompleted(sender As System.Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
        Timer1.Stop()

    End Sub






This is all of the code that is behind the accounthistoryDLG.vb Dialog form..
I call the dialog form from the main page using accounthistorydlg.showdialog
0
 
LVL 86

Accepted Solution

by:
Mike Tomlinson earned 2000 total points
ID: 38420014
Try changing your Tick() event to:
    Private Sub Timer1_Tick(sender As System.Object, e As System.EventArgs) Handles Timer1.Tick
        ToolStripStatusLabel1.Text = DateTime.Now.ToString("D")
        Debug.Print(DateTime.Now) ' <-- should appear in the "Immediate" pane of the IDE
    End Sub

Open in new window


Also, did you set the WorkerReportsProgress() property of your BackgroundWorker() to True?
0
 

Author Closing Comment

by:ConnexusDave
ID: 38422772
YEA this works so cool.. not only do I have a clock to show it is working but I can now move the form around and it repaints immedialy.. THANKS SO MUCH YOU DA MAN!!!!!!!
0
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 38422795
Glad you got it all figured out...   =)

I take it the WorkerReportsProgress() property was not set?
0

Featured Post

Vote for the Most Valuable Expert

It’s time to recognize experts that go above and beyond with helpful solutions and engagement on site. Choose from the top experts in the Hall of Fame or on the right rail of your favorite topic page. Look for the blue “Nominate” button on their profile to vote.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

How to remove superseded packages in windows w60 or w61 installation media (.wim) or online system to prevent unnecessary space. w60 means Windows Vista or Windows Server 2008. w61 means Windows 7 or Windows Server 2008 R2. There are various …
Simulator games are perfect for generating sample realistic data streams, especially for learning data analysis. It is even useful for demoing offerings such as Azure stream analytics, PowerBI etc.
This video teaches viewers about errors in exception handling.
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
Suggested Courses
Course of the Month19 days, 9 hours left to enroll

872 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question