Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations John Tel on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Multiple instances of the same form?

Status
Not open for further replies.

sunil128

Programmer
Mar 20, 2008
45
GB
Hi all, another very newbie question so apologies in advance, but how do I get multple instances of a form to work?

The form (form2) is called by pressing a command button on a another form (Form1).Currently if i go back to Form1 and press the cmdbutton it doesnt create another instance of the form2, it just reverts focus back to the exixting instance.
Thanks
 
Harley,
Thanks. I prefer to use the custom property versus the tag just because I can give it a more descriptive name. Both Accomplish the same idea.
In vb6 does a form instance get added to a standard Forms collection? I know it does not in VBA. Each instance shares the same name, and in Access only the form instance opened with the docmd.open form or manually is added.

the original code

For Each oForm In Forms
If oForm.Name = "Form2" Then
iCount = 1
oform.setfocus
oForm.SaveRecordMethod
End If
Next

would not work in VBA for 2 reasons. All instances share the same name and only one instance is added to the forms collection. Thus I have to use a user defined collection.
 
MajP,

Yes, forms created at runtime are added into the standard Forms collection.

The VB Forms collection will allow multiple forms with the same name to exist within it at any one time, so the code will loop all open forms and close any that have the name "Form2".

Agreed, the tag and custom property you mention do pretty much the same thing in this instance and it's totally down to developers choice [smile]

HarleyQuinn
---------------------------------
The most overlooked advantage to owning a computer is that if they foul up there's no law against wacking them around a little. - Joe Martin

Get the most out of Tek-Tips, read FAQ222-2244 before posting.
 
Sorry guys im still about confused here, who's code should i actually use? HarleyQuinns but with the tag property? His makes a bit more sense to me.
 
Harely,
Thanks. I misread the original post. Sunil has multiple instances of Form2 and he/she wants to close all instance of Form2. I thought that they needed to manage a specific Form2 instance. So their loop would make sense.

So no need for a collection since the insitu Forms collection works. So I guess I did add a little confusion.
 
sunil128,

I'd go with my code, but then I would say that [wink]

Seriously though, as MajP said, alot of his code is VBA to get around not being in the Forms collection, and unless you need to specifically identify one instance of the Forms2's you've created for some reason there's no need to use the .Tag property (though it won't hurt anything if you do set it).

The example I'd use in your situation would be the code ca8msm posted on my behalf above.

Hope this helps

HarleyQuinn
---------------------------------
The most overlooked advantage to owning a computer is that if they foul up there's no law against wacking them around a little. - Joe Martin

Get the most out of Tek-Tips, read FAQ222-2244 before posting.
 
>> only one instance is added to the forms collection

This is not true for VB. Multiple instances are added. The name of each form would be the same, but in this case, it shouldn't matter. You see, if there are 3 "Form2" forms open and 1 Form1, the Forms.Count property will be 4. If you loop over the forms and call the .SaveRecordMethod, that subroutine will be called on each form.

I prefer to reduce the number of times I need to access the form externally. This allows me to encapsulate the code as much as possible.

If this were my project, I would...

1. Have a source code module that has all the load/save procedures.

2. Have the Form2 (customer form) know how to load and save it's own data.

3. I would have the main form (form1) call a public sub on form2. This sub would load the data and then show the form.

4. I would put the save routine in the QueryUnload event.

There are a couple of tricks that you need to know in order to effectively implement this.

Number 1 is easy. Just add a module and write some public sub/function that returns and saves data.

Number 2 is not so obvious, but it is pretty easy once you get the hang of it.

In form2

Code:
Private lCustomerId As Long

Public Sub ShowForm(ByVal CustomerId As Long)

    lCustomerId = CustomerId

    ' Code here to load the data and set properties

    Call Show

End Sub

Then, in form1 (your main form)...

Code:
Private Sub Command1_Click()

    Dim oForm As Form2

    Set oForm = New Form2
    Call oForm.ShowForm(YourCustomerIdNumber)
    Set oForm = Nothing

End Sub

Notice the minor difference here. Now, the main form creates an instance of form 2 and calls a public subroutine. Form 2 then loads data, sets properties (captions & text boxes), and then shows itself. The main form does NOT show the sub-form, but causes it to be shown be calling the ShowForm method.

The next 'trick' is using the QueryUnload event to save your data. This is really an awesome event to use. Whenever your form closes, it will call this event. You then have the opportunity to do stuff (like saving data). What's cool about this event is that you can CANCEL it.

The QueryUnload event has 2 byref arguments. The first is Cancel. The default value for this argument is 0. If you set it to any value other then 0, the form will not close. the second parameter is UnloadMode. This argument tells you what caused the QueryUnload.

[tt][blue]
vbFormControlMenu 0 The user chose the Close command from the Control menu on the form.
vbFormCode 1 The Unload statement is invoked from code.
vbAppWindows 2 The current Microsoft Windows operating environment session is ending.
vbAppTaskManager 3 The Microsoft Windows Task Manager is closing the application.
vbFormMDIForm 4 An MDI child form is closing because the MDI form is closing.
vbFormOwner 5 A form is closing because its owner is closing.
[/blue][/tt]

Now that you can capture this event, you can validate data and save. You can even cancel the unload process if the data does not validate.

To test this, create a new VB6 project with 2 forms (Form1 & Form2).

On form 1, put a command button (named Command1).
Then, copy/paste this code.

Code:
Option Explicit

Private Sub Command1_Click()
    
    Dim oForm As Form2
    
    Set oForm = New Form2
    Call oForm.ShowForm(6) ' 6 represents your customer id
    Set oForm = Nothing
    
End Sub

Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
    
    Dim oForm As Form
    
    For Each oForm In Forms
        If oForm.Name = "Form2" Then
            Unload oForm
        End If
    Next
    
    If Forms.Count > 1 Then
        Cancel = 1
    End If
    
End Sub

On form2, put 2 command buttons and name then btnCancel and btnSave. Put 2 text boxes (Text1 and Text2).

Then copy/paste this code.

Code:
Option Explicit

Private lCustomerId As Long
Private UserCancel As Boolean

Public Sub ShowForm(ByVal CustomerId As Long)
    
    UserCancel = False
    lCustomerId = CustomerId
    ' Code to load the data and populate the controls on the form
    Call Show
    
End Sub

Private Sub btnCancel_Click()
    
    UserCancel = True
    Unload Me
    
End Sub

Private Sub btnSave_Click()
    
    Unload Me
    
End Sub

Private Function ValidateData() As Boolean

    ValidateData = True
    
    If Not IsNumeric(Text1.Text) Then
        ValidateData = False
    End If
    
    If Not IsDate(Text2.Text) Then
        ValidateData = False
    End If

End Function

Private Sub SaveData()
    
    ' code to save the form data
    
End Sub

Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
    
    If UserCancel Then
        Exit Sub
    End If
    
    If Not ValidateData Then
        Call MsgBox("Your data does not validate.  Unable to save.", vbInformation, "Save Data Error")
        Cancel = 1
        Exit Sub
    End If
    
    Call SaveData
    
End Sub

Make sense?


-George

"The great things about standards is that there are so many to choose from." - Fortune Cookie Wisdom
 
I should mention that the code I posted above will cause your data to be saved when you click the X in the upper right corner of the form.

In my opinion, clicking the X should be the same as Cancel (don't save). It's not to difficult to accommodate that if that is the desired behavior.


-George

"The great things about standards is that there are so many to choose from." - Fortune Cookie Wisdom
 
Ive kind of muddled around and got my unloads to work now, so thanks everyone. However i still have an issue with the very first code snippet that george provided (below) which actually created the instances in the first place:

Code:
Private Sub Command1_Click()
    
    Dim oForm As Form2
    
    Set oForm = New Form2
    Call oForm.Show(vbModeless)
    Set oForm = Nothing
    
End Sub

Apologies for my (continued) lack of understanding here but I still dont get how you refernce oForm in other parts of the program if your setting oForm to nothing as it does in the code? The only reason i think im getting the whole thing to work is because ive set oForm up as public object in my global.bas and never setting it to nothing.
 
Since it is causing you a lot of confusion, remove it. You shouldn't have any line of code in your app that you don't understand.

-George

"The great things about standards is that there are so many to choose from." - Fortune Cookie Wisdom
 
Here is my conceptual understanding of this, but probably not technically correct.
A form is a class object like other objects, but special in that it has a visible component. When you load a form instance vb automatically assigns it to the Forms collection. So the object variable "oForm" is a pointer to the visible component. When you set oForm to nothing the pointer is set to nothing put the Forms collection still has a pointer to the visible object. So you can reference it from the Forms collection. When you unload the form the pointer is removed from the Forms collection automatically.
I know this is not technically correct, but conceptually it is the right idea.
 
If you need to reference the form outside of the subroutine that creates it, then your current method of declaring it globally is correct. Like any other object, the scope you give it and when you release the reference to it is determined by where in the program you need to use it.

 
If you are working with multiple instances of a form you should not define a global form variable, but use a collection to manage them. Hurley and gmmastros have shown this. Now I learned that in VB6 you can use the native forms collection and do not need to create your own.

However, I personally would create my own global collection for my Form2. Then if there is something in the collection I can handle it. If I use the native Forms collection I have to check that the item is a Form2 before handling it.

I personally would consider taking what gmmastros has given and building a
public Form2s as collection

Here is a good example:

However, gmmastros solution is fine as is. He just manages his forms instances with the native Forms collection (not a global Form2 variable).
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top