When you dim a variable it gets a default value. Numeric variables default to 0
In your sub you change the value to 1 but on the line after your debug.print so the first time it will = 0 and every time after it will =1 unless of course you change it somewhere else or unload the form.
Not sure what you mean by downloaded as there is nothing there that downloads anything.
when form2 is unload value should go back to 0, but this does not happen check it out
When forms are unloaded, their GUI portions are unloaded: controls, menus, etc. Declarations are not reset. As mentioned, setting the form to Nothing after its unloaded will reset those variables. If you don't do that, then you should manually reset the variables during the form's load or unload event
Insomnia is just a byproduct of, "It can't be done"
Now I understand the importance of erase array or set object = nothing
Especially if those variables are objects like open ADO connections/recordsets, references to other VB objects, collections, etc. In those cases, I will set them to Nothing in Form_Unload as a standard practice
Insomnia is just a byproduct of, "It can't be done"
Especially if those variables are objects like open ADO connections/recordsets, references to other VB objects, collections, etc. In those cases, I will set them to Nothing in Form_Unload as a standard practice
Maybe I don't understand what you are getting at... but that doesn't seem to be the case.
Consider this small test case: two forms and a class. The secondary form creates an instance of the class with form scope and "opens" it on load. "closes" it on unload. Seems to work exactly as one would expect with no need to release and re-create objects.
Add 2 forms: Form1, Form2
Add 1 Class: Class1
In class1 terminate event: Debug.Print "Terminate"
In form2: Dim c As Class1 in declarations. In form load: Set c = New Class1
In form1: Form_Click event: Form2.Show
Run project & click Form1 to launch Form2. Now close Form2 while Form1 still open. No terminate event from the class. Closing Form1 ends the project so all references are released at that point
Insomnia is just a byproduct of, "It can't be done"
I'm going to weigh in on this one as well. LeandroA, we must think of a form as having two pieces: 1) the user interface (UI) piece, and 2) the code/COM piece.
The code/COM piece is exactly like a class (CLS) module. It gets instantiated, you can uninstantiate it, and you can use its methods while it's instantiated.
Now, the UI piece is what actually gets loaded/unloaded. However, as a rule, the code/COM piece virtually always gets instantiated when the UI piece gets loaded. However, the opposite is not at all true. When you unload the UI piece (with something like Unload Form1), the code/COM piece typically stays instantiated.
Now, there's another "trick" going on here as well. These (FRM) modules have a hidden property named VB_PredeclaredId always set to TRUE. For typical class (CLS) modules, this VB_PredeclaredId property is typically FALSE, but we can set it to TRUE if we like.
When VB_PredeclaredId=TRUE, what does that mean? It means we get an implicit object variable for the form that's the same as the name of the form. That's why things like Load Form1, or Form1.Show, or Unload Form1 work. This implicit object variable is quite similar to an object variable declared with the New keyword (eg, Dim MyObj As New MyClass). Anytime you "touch" these VB_PredeclaredId=TRUE classes (including FRM classes), if they're not instantiated, they'll auto-instantiate (just like the object variables declared with the New keyword).
So, a FRM module has two pieces, the UI and the code/COM piece. So, if you'd like to continue using that implicit object variable (that's always the same as the form's name), when you unload the form, you must also uninstantiate the the code/COM piece. To do that, here's a statement I often place in my form's unload event:
Code:
Private Sub Form_Unload(Cancel As Integer)
Set Form1 = Nothing ' This clears the Form1 object variable, which will probably uninstantiate this form once this event finishes.
End Sub
Now, note that you must use the implicit object variable's (i.e., form's) name. You can not say:
Code:
Private Sub Form_Unload(Cancel As Integer)
Set Me = Nothing ' Bad!!!!
End Sub
To appreciate this, we must remember how COM objects work. They auto-uninstantiate when all references to them are gone (i.e., all object variables are set to Nothing). (To be technically correct, their internal reference count has to go to zero.) And the Me keyword is a separate reference from the Form1 object variable. We can set Me to Nothing, but Form1 will still be referencing the object.
(To avoid this confusion, many recommend not using these implicit object variable names. But personally, I have no problems with them. We must simply understand how they work.)
And lastly, to directly address your question, all module level variables you declare in a form's code will belong to the code's/COM's instantiation, and have very little to do with the UI portion of the form. And that's why you're seeing what you see.
------------
EDIT1: Also, just as a further FYI, events like Form_Load, Form_Unload, and Form_Activate really have more to do with the UI. However they are code, and therefore, they're instantiated with the code/COM part of a form. Now, in contrast, events like Form_Initialize and Form_Terminate are only concerned with the code/COM portion and have nothing to do with the UI portion of a form.
EDIT2: Also, if you edit a FRM file with something like Notepad, you can clearly see the two section (UI and code/COM). Also, you can see that VB_PredeclaredId property.
Last edited by Elroy; Dec 2nd, 2019 at 03:44 PM.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
Option Explicit
Private Sub Form_Click()
Me.Caption = Forms.Count
Form2.Mostrar ' FYI, this is auto-instantiating Form2 if it's not already instantiated.
End Sub
Code:
Option Explicit
Dim value As Long
Public Sub Mostrar()
Debug.Print value ' Always prints 0 now.
value = 1
Me.Show vbModal
Set Form2 = Nothing ' The fix.
End Sub
Last edited by Elroy; Dec 2nd, 2019 at 02:37 PM.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
This issue could be confusing sometimes.
I'll also add an explanation, how I understand it.
I see the forms as two object coupled together. Both with the same name.
One is very much as a class module and the other is the UI.
When you add a new form to the project, lets say "Form2", two objects are added or declared.
One Form2[UI] object and another Form2[class] object.
The Form2[UI] object can be loaded and unloaded.
The Form2[class] object can be instantiated and released (like a class module).
You have these events in the form code module to see when those thing happen:
Form_Load
Form_Unload
Form_Initialize
Form_Terminate
Any attempt to access a member that reference something of the UI of Form2, like for instance Form2.caption will load both (if they are not already loaded), first Form2[class] and then Form2[UI].
But if you reference something that is not of the UI, but only deals with variables (for instance a public property that you added to Form2's code), it will load only the Form2[class], and Form2[UI] will remain not loaded.
Then when the user closes the form, only the UI part is unloaded, but Form2[class] remains instantiated.
Like a class module, the way to de-instantiate it is to set it to Nothing or when it goes out of scope.
And now one can understand better what you are doing when declaring a form variable As New, for example:
Code:
Private Sub Command1_Click()
Dim f As Form2
Set f = New Form2
f.Show 1
End Sub
The Form2[Class] object fires the terminate event when it is closed, because it loses all his references.
That does not happen is you use it without declaring the variable and setting is As New.
Because vb helps the programmer by doing all the declaration and instancing automatically.
When you use Form2 (without As New), VB does the equivalent as you've done:
In a Module:
Code:
Public Form2 As New Form2
Think that every time you add a form, VB adds a line like that.
VB automatically instantiate a new instance of Form2 and keeps the reference in the global variable Form2.
So you can unload the UI part, but unless you set the Form2[class] to Nothing explicitly, it never goes out of scope, and the Form_Terminate event only fires before the program ends.
Last edited by Eduardo-; Dec 2nd, 2019 at 03:50 PM.
I agree that VB6 internally adds some Public Form2 As New Form2 line when we add a form to our project (although it doesn't show it to us).
Also, Eduardo mentioned "going out of scope". However, these VB_PredeclaredId=TRUE object variables (i.e., like Form2) are global and never go out of scope (until the program ends). So, we must explicitly clear them by setting them to Nothing.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
Also, Eduardo mentioned "going out of scope". However, these VB_PredeclaredId=TRUE object variables (i.e., like Form2) are global and never go out of scope (until the program ends). So, we must explicitly clear them by setting them to Nothing.
Exactly, a global variable in a module never goes out of scope, that's why we need to set it explicitly to Nothing if we want the Form[class] object to be de-instantiated.
Some suggest to never use the "original" form (like Form2) and always use a new one by declaring variables and instancing the forms explicitly... other suggest to always set the forms to nothing when closed... they could be good advices, but if one understands how these objects work, you can use the "auto-declared" form instancing if you want and you could want to keep some variables in the form[class] and not to set it to Nothing as long as you know what you are doing.
Oh sure, anything with VB_PredeclaredId = True gives you that "free" instance and it is global and created with "As New" semantics. So of course these behave differently from other instances.
LeandroA,
It doesn't look like anyone else has mentioned it, but for future reference:
You're using the word download when you mean to use the word unload.
The form is being unloaded and you expect the variables to be unloaded too.
Download means something is transferred from a source outside your computer to your computer, usually in the context of downloading a file from the Internet.
Your incorrect use of Download was confusing, and you continued to use it even once everyone else figured out what you meant, so I felt I should let you know.
As always, I'm sure your use of English is much better than the use of any other language that I might choose would be.
"Anyone can do any amount of work, provided it isn't the work he is supposed to be doing at that moment" Robert Benchley, 1930
Some suggest to never use the "original" form (like Form2) and always use a new one by declaring variables and instancing the forms explicitly... other suggest to always set the forms to nothing when closed... they could be good advices, but if one understands how these objects work, you can use the "auto-declared" form instancing if you want and you could want to keep some variables in the form[class] and not to set it to Nothing as long as you know what you are doing.
Always use separate new form instances. Global Form1 predeclared instances are a left-over from MS Access times, where you can write a query w/ something like WHERE MyColumn = Form1!MyTextBox which is awful on so many levels.
When you use Dim f As Form1 : Set f = New Form1 : f.Mostrar it's not possible to set f variable to Nothing in Form_Unload, can you? Just don't use global predeclared instances so that you never have to set the global Form1 references to Nothing too.
I cannot think of *any* modern language (not just .Net ones) that promotes global state so blatantly as global predeclared form instances as implemented in VB6. And those predeclared classes are next to eradicate, just don't :-))
@LaVolpe: My idea is that setting Form1 to Nothing is not something Form_Unload has to do on the principle of a separation of concerns.
The way setting local f variable to Nothing is concern of the caller code and is not even *possible* in form's code (fiddling with local stack variables of the caller) so setting Form1 global reference to Nothing is tangling concerns in a subvertive way.
It is a good practice (and more manageable) for the piece of code that instantiated anything to manage this instance lifetime. That's way global predeclare auto-instantiating forms/classes are so ofthen back-firing by surprise on program tear-down.
IDK ... I seem to be the odd-man-out on this one (maybe with Eduardo joining me). But we've got these global object variables, and I never saw a reason to not use them.
Originally Posted by wqweto
It is a good practice (and more manageable) for the piece of code that instantiated anything to manage this instance lifetime.
And, if we like, we can certainly have the caller manage these global object variables. Just as an example, the OP's code could be reworked as follows:
Form1 code:
Code:
Option Explicit
Private Sub Form_Click()
Set Form2 = Form2 ' Instantiate Form2's COM.
Form2.Show vbModal ' Load and show Form2's UI (modally).
Set Form2 = Nothing ' Uninstantiate Form2's COM.
End Sub
Form2 code:
Code:
Option Explicit
Dim value As Long
Private Sub Form_Load()
Debug.Print value
value = 1
End Sub
Here, we've let the Form1 procedure take care of uninstantiating the Form2's COM piece. I also explicitly instantiated it, although that's not necessary as it will auto-instantiate.
Now, this gets a bit more complex if we don't load Form2 modally, but uninstantiation could still be handled by the caller if done correctly.
In my primary project (with 100s of forms), in 99% of the cases, a form will always and only get one instance (with the UI loaded only once). So, I just never saw a reason to create a second object variable when I've already got one.
Again, many ways to skin a cat, and nothing inherently wrong with different approaches so long as they get the job done.
Best Regards,
Elroy
Last edited by Elroy; Dec 3rd, 2019 at 10:36 AM.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
Always use separate new form instances. Global Form1 predeclared instances are a left-over from MS Access times, where you can write a query w/ something like WHERE MyColumn = Form1!MyTextBox which is awful on so many levels.
I've always thought that the predeclared feature of the forms was in the sake of RADness, I mean to make VB simpler for the programmers.
I agree that it can lead to problems sometimes.
And I agree that in general, it is preferable to use "custom" instances, because you have more clear control over the life cycle.
But what would someone have to do it he needs a global instance of some form, and only needs to use this global instance and no others?
He would have to declare in a module:
Code:
Public MyForm1 as New Form1
But... VB already offers that option... by default.
This brings to mind another one: never declare objects "As New".
And regarding instantiation, when you instantiate an object like:
Code:
Dim F as Form1
Set F = New Form1
F.Show vbModal
there is another one: "always set the object variables to Nothing after using them".
They are in general good advices for good practice and to avoid errors, like other ones like "never use Goto" or some other rules that are good rules, but that IMO don't have to be enforced as hard rules, there is some place for those practices as long as you take care.
Originally Posted by wqweto
When you use Dim f As Form1 : Set f = New Form1 : f.Mostrar it's not possible to set f variable to Nothing in Form_Unload, can you? Just don't use global predeclared instances so that you never have to set the global Form1 references to Nothing too.
That's sometimes necessary if you use a global variable for the form (and it would be the same if you used the predeclared variable Form1 or another one added by you like MyForm1) to set the form variable to Nothing when it closes in the case that the form is shown not modally.
I don't see many others ways to do it.
Originally Posted by wqweto
I cannot think of *any* modern language (not just .Net ones) that promotes global state so blatantly as global predeclared form instances as implemented in VB6. And those predeclared classes are next to eradicate, just don't :-))
cheers,
</wqw>
May be in the next version.
EDit: Eh, no, it must be backward compatible.
Last edited by Eduardo-; Dec 3rd, 2019 at 01:19 PM.
@Elroy: Think about Form2.Show vbModal line. This reduces Form2 to a dummy View in the Model-View-Presenter design-pattern, a module which has no code at all (besides simple client-side validation probably).
If Form2 was coded as a Presenter then there has to be a method that accepts input data, fills the controls, calls Show vbModal and returns true/false if the form is confirmed along with output data. The modal interaction is encapsulated and the public methods on the form that other components are calling are reduced to a single method. There is no need for anyone to know what TextBoxes are there in the UI and all the gory details of the UI. This way the UI can be refactored separately in insolation without breaking a bunch of other modules/forms that access controls on Form2 for whatever reason.
I generally use the default instance and can't say that I ever ran into any issues as a result. There are of course times when I create a new instance of a form but more often than not I see no reason to not use the default instance.
Always use separate new form instances. Global Form1 predeclared instances are a left-over from MS Access times,
Originally Posted by Eduardo-
I've always thought that the predeclared feature of the forms was in the sake of RADness, I mean to make VB simpler for the programmers.
I agree that it can lead to problems sometimes.
Default instances have been a part of VB since the beginning... they don't have their roots in anything but the beginning of VB. Their existence in VB6 is from VB5, which is from VB4, which is from VB3, which is from... well you get the idea. It's in .NET because it was put back in. Originally it wasn't there. In NET 1.0 and 1.2 and 1.5 it wasn't there. But so many people complained about it, that it found its way back in in time for FW2.0... personaly I think it should have been left out.. but eh... it is what it is, and I don't use it.
Originally Posted by DataMiser
I generally use the default instance and can't say that I ever ran into any issues as a result. There are of course times when I create a new instance of a form but more often than not I see no reason to not use the default instance.
I'm on the other side. A lot of the apps I've worked on are multi screened, so using the default instance of the form would never work. I need multiple instances to show different sets of data at various times. Default instances wouldn't work in those cases, only by creating new instances of the forms and passing it the data to display would that work.
I still don't use default instances, but it's mostly out of habit than anything else. It's not out of any particular despise or hatred for them, it's just muscle memory this point. I need a form, I create a variable, instanciate it, then show it. When I'm done with it, I dispose of it.
@Elroy: Think about Form2.Show vbModal line. This reduces Form2 to a dummy View in the Model-View-Presenter design-pattern, a module which has no code at all (besides simple client-side validation probably).
If Form2 was coded as a Presenter then there has to be a method that accepts input data, fills the controls, calls Show vbModal and returns true/false if the form is confirmed along with output data. The modal interaction is encapsulated and the public methods on the form that other components are calling are reduced to a single method. There is no need for anyone to know what TextBoxes are there in the UI and all the gory details of the UI. This way the UI can be refactored separately in insolation without breaking a bunch of other modules/forms that access controls on Form2 for whatever reason.
Yeah, I suppose I hear ya. I'm being sucked into a huge project for my primary client where they're talking about a huge upgrade. Apparently we're going to .NET. But, in the process, they're throwing around terms like N-type architecture and server-side-processing, and flexible data entry forms. I guess what you're saying is somewhat similar to what they're suggesting, detaching all the functional components of the program from each other. (And I'm now glad that I've never used a bound-control.)
And, I guess I'm just too steeped in the ways of VB6. We'll see how much I'll be a part of this big upgrade. Just as an FYI, SQL server over a WAN among several sites is also a part of it. At this point, it's unclear if the .NET translation or the upgrade to a SQL backend will come first.
Want to help? (He says only half kidding.)
Also, I think we've beaten this horse enough. I'll bow out of this one.
Y'all Take Care
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.