Click here to Skip to main content
15,888,239 members
Please Sign up or sign in to vote.
2.00/5 (1 vote)
See more:
I'll try to explain my issue best I can. I'm not an expert on vb.net but have built a number of applications. I am currently working on an app that overlays a booking program. the app opens with two forms.
The first form is a permanent form that stays open and allows the booking agents to make various commands to the booking program.
The other form runs various methods to change reservations in the underlying program. When an options on the second form is checked then this code is run:
VB
Private Sub frmOWTMain_KeyUp(ByVal sender As Object, ByVal e As
   System.Windows.Forms.KeyEventArgs) Handles Me.KeyUp
       If e.KeyCode = Keys.Enter Then
           Me.Hide()
           If Me.cbFare.Checked Then
               Call doFareSavings()
               Me.cbFare.Checked = false
           End If
           Me.Show()
       End If
End Sub


This is the called subroutine:
VB
Private Sub doFareSavings()
       Dim myPrice As New Pricing
       If myPrice.checkForFS = False Then '' code that checks for which
                                          '' form to  show
           frmFS.Show()
       ElseIf myPrice.checkForFS = True Then
           frmFSVerify.Show()
       End If
End Sub


The issue I'm having is that I want the frmOWTMain to show after the subroutine is run, but while the subroutine is running it calls other forms and the Me.show runs prematurely.
I can't figure out how to make sure that all the subroutines (and there are quite a few) are run before the Me.show in the calling routine runs. This is driving me crazy because I know there is a simple solution that is eluding me. I've searched all over the web, but have been unable to find and answer. Thanks in advance for any help

John Murray
Posted
Updated 28-Sep-14 16:18pm
v3

You could call the form with ShowDialog - this would block the execution of doFareSavings until you close the form (and any other forms shown later)
 
Share this answer
 
I think this would work for you. It has a timeout value and you can decide what to do if it times out. .
VB
Private Sub doFareSavings()
       Dim myPrice As New Pricing
       If myPrice.checkForFS = False Then ' code that checks for which
                                          ' form to  show
           frmFS.bolCompletedWork = False
           frmFS.Show()

            'InsertedCode
            Dim TimeOutSeconds As Integer = 10
            Dim TimeOutTime As Date = DateAdd(DateInterval.Second, TimeOutSeconds, Now)
            While Now < TimeOutTime
                If frmFS.bolCompletedWork Then
                    'do anything you like here
                    Exit Sub
                End If
            End While
            'Things have timed out so put some fail safe code here
            MsgBox("timeout")


       ElseIf myPrice.checkForFS = True Then
           frmFSVerify.bolCompletedWork = False
           frmFSVerify.Show()

            'InsertedCode
            Dim TimeOutSeconds As Integer = 10
            Dim TimeOutTime As Date = DateAdd(DateInterval.Second, TimeOutSeconds, Now)
            While Now < TimeOutTime
                If frmFSVerify.bolCompletedWork Then
                    'do anything you like here
                    Exit Sub
                End If
            End While
            'Things have timed out so put some fail safe code here
            MsgBox("timeout")


       End If
End Sub

Hope this helps.
 
Share this answer
 
v3
The way I would handle something like this would be to put all the doFairSavings() functionality on a background thread. I don't think I would make the calling form invisible either. Instead I would put some progress information, or a "busy" animation on it. Then, when doFairSavings() is complete, I would have it call back into the UI thread using a delegate and me.invoke() so I could update any form elements I needed to without worrying about cross thread errors. Then I would start whatever forms need to be shown.

I'm not sure what you may know about this subject, so I'm going go over everything you will need to know to understand the example I'm going to give (at the bottom).

Ok, a little about threads.

You should think of them as a separate program that is connected to yours only by shared memory. This means that public variables you declare will be accessible to any threads you create, but accessing these variables by multiple threads can be tricky. Multiple threads writing to a variable can cause the data contained in those variables to become corrupt, or in some cases cause numeric variables to contain values you didn't want, and didn't expect. So when reading to or writing to variables from multiple threads, you should wrap those calls in a synclock statement.

When you create a thread in vb.net, you assign the thread a Sub or Function to live in, like this:

VB
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

    Dim thisNewThread As New Threading.Thread(AddressOf BackgroundThread)
    thisNewThread .Start()

End Sub

Private Sub BackgroundThread()
    ' All code here is running on the new thread!
End Sub


In the code above, when you click on Button1 a new thread will be created and begin to run in the Sub BackgroundThread(). The thread has access to any variables in that Sub's scope - meaning if BackgroundThread() is in a form you'd created, then it would have access to any functions, private or public variables and objects declared in that form, or public variables and objects declared in your modules.

One rule of thumb for your created threads is that they can not access controls on your form. Controls on your form were created by the UI thread, and only the UI thread can read from or write to those controls. If you try to change the text of your form's textbox from the new thread, a cross thread violation exeption will (probably) be thrown in your application.

We get around this by using DELEGATES, .InvokeRequired() and .Invoke().

A delegate is a way for you to save the address of a Sub or function and pass it to a function that lives elsewhere. Then the method that has the delegate can use it, and program execution returns to the saved function - even if the code that's using the delegate is within another class or object.

It works like this:

VB
Public Class Form1

    Private Delegate Sub OurPrototype(ByVal message As String)
    Private theCallback As OurPrototype

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

        'Dim t As New Threading.Thread(AddressOf BackgroundThread)
        't.Start()

        theCallback = AddressOf Callback
        theCallback("hello")
    End Sub

    'Private Sub BackgroundThread()

    'End Sub

    Private Sub Callback(ByVal message As String)
        MsgBox(message)
    End Sub
End Class


This is all the code from a new form in a new project. At the top, we've created a delegate prototype. It's the signature, or format of the sub or function that will be contained in delegates created using this prototype. You'll see that it matches the Sub that we assigned to our delegate in the button click event. If you were to click the button, a message box would pop up containing the text "hello".

I know it doesn't seem very useful right now, but you could pass that delegate as a parameter to an object you've created from one of your classes, and when the delegate is called from the depths of that class, program execution would return to that function, and you'd see the message box containing the text "hello".

You can also use it to change threads using .Invoke() - and that's really where the magic is.

Most controls and all forms contain the .Invoke() and .InvokeRequired() functions. .InvokeRequired() returns a boolean value indicating weather or not your current thread is the UI thread. If it's not, and you need to be on the UI thread, you use .Invoke(), like this:

VB
Private Sub Callback(ByVal message As String)

    If Me.InvokeRequired Then
        Me.Invoke(theCallback, message)
    Else
        Me.Text = message
    End If

End Sub


Here's our callback function again, only this time if it's called by our new thread, me.InvokeRequired() will be True. If that's the case, then the callback sub will be run AGAIN, but this time it will be on the UI thread. The second time through, .InvokeRequired() will be False, and the code in the else portion of that if statement will run - setting the text on your form to whatever's in the message string (in our case it will be "hello").

Ok, that's the background stuff you should know to understand this example code... Here it is:

VB
 Private Delegate Sub OurPrototype(ByVal message As String)
 Private theCallback As OurPrototype

 Private Sub frmOWTMain_KeyUp(ByVal sender As Object, ByVal e As
    System.Windows.Forms.KeyEventArgs) Handles Me.KeyUp
        If e.KeyCode = Keys.Enter Then
            Me.Hide()
            If Me.cbFare.Checked Then
                theCallback = AddressOf Callback

                Dim bgThread as New Thread(AddressOf ourThread)
                bgThread.Start()

                'Call doFareSavings()
                'Me.cbFare.Checked = false
            End If
            'Me.Show()
        End If
 End Sub

Private Sub ourThread()
    ' Do your doFareSavings() work here. When your done, call your delegate:
    theCallback("you can incluse information in this string about what form to show here")
End Sub

 Private Sub Callback(ByVal message As String)

     If Me.InvokeRequired Then
         Me.Invoke(theCallback, message)
     Else
         ' Show whatever form you like here, directed by the message string
         Me.cbFare.Checked = false
         If message = "whateverForm" Then whateverForm.show()
         Me.Show()
     End If
        
 End Sub


Doing it like this, your form will not show until all the work is done.

By the way - I hope you realize that this is nowhere NEAR all the information on threading and delegates. It's just the basics. If you want to know more, try Google or MSDN. There's a lot more to know about both.

-Pete
 
Share this answer
 
v5
Comments
Travlintom 1-Oct-14 18:57pm    
Sorry Pete, did not see your response as I was away. I would like to see example code as I have never done any threading and it seems complicated to me. I will look into learning it though. Thanks again.
pdoxtader 1-Oct-14 19:00pm    
Threading is easy... At least I think so. I'll post some example code in a few.

-Pete
Travlintom 1-Oct-14 19:44pm    
Thanks Pete, I really appreciate it. Can you run two forms on two separate threads? And if you do, do modal forms interrupt the threads? Just curious. Thanks again.
John
pdoxtader 1-Oct-14 20:03pm    
You wouldn't attempt to run forms on separate threads... and wouldn't need to. You can create threads, do whatever work you need, and then update whatever form you need to using a delegate and .Invoke() the way I just described above.

-Pete
Travlintom 1-Oct-14 22:40pm    
Pete, thank you so much. I will start testing it to try to get a handle on it. It does seem formidable to me. But I can certainly see the benefits of using a thread. Thanks for the time you spent on this. I can't tell you how much I appreciate it.
John
Any time you have to do something in alot of places, just build a reusable sub or function for it
VB
Public Function OpenNewForm(Of TypeOfForm As {New, Form})(Optional ByVal TimeOutSeconds As Integer = 10) As Boolean
    Dim f = New TypeOfForm()
    f.Show()

    Dim TimeOutTime As Date = DateAdd(DateInterval.Second, TimeOutSeconds, Now)
    While Now < TimeOutTime
        If f.bolCompletedWork Then
            'do anything you like here
            Return True ' success
        End If
    End While

    Return False ' Timed out
End Function

Call it like this...
VB
If OpenNewForm(Of Form1)(5) Then
    'success
Else
    'failure
End If
 
Share this answer
 
Comments
Travlintom 30-Sep-14 17:52pm    
Davester, Thanks, great solution but it stil errors out at the "f.bolCompleteWork" portion of the code. Is this a variable I should be creating? I don't see it referred to in any way in the function.
Davester28 2-Oct-14 9:00am    
Yes, in your other form that does the work.... Right after the first line...

(Public Class FrmWhateverYourFormNameIs)

Put a line as follows...

Public bolCompletedWork as boolean = False

(Public is like Dim but viewable by other forms.)
Later in your code, when the work is finished, put bolCompletedWork = True.

Sorry I didn't explain that better.

Davester28 20-Oct-14 11:20am    
How did you end up handling this ?
Travlintom 20-Oct-14 13:45pm    
Davester - I basically used your solution #6. I created two functions. The first checks to see if any forms are open, the second if any checkboxes are checked. I combinded them in one more function that states if no forms are open and no boxes checked then it opens the main form. Otherwise it goes and opens up the next checkbox. Works perfectly, I just have to put it right after each process finishes. I have also started working with some test threads in a different app and really appreciate your code. Thanks again.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900