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 Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

How do I tace what is nulling my object? 1

Status
Not open for further replies.

1DMF

Programmer
Jan 18, 2005
8,795
0
0
GB
ok it wasn't being nulled, but it seems the class of a sub form is not directly accessible.

Why is
Code:
Form_Drop_Email.oFileWatcher
nothing

but
Code:
Forms!Check_Case.Form!Nav_Subform.Form.oFileWatcher
is not?

2010 Nav Control behaves oddly, why are the source form objects not available (navigation target name)?

Why isn't that
Code:
Forms!Check_Case.Form!Nav_Subform.Form![highlight #FCE94F]Drop_Email[/highlight].Form.oFileWatcher

----------------------

Basically I have a public object oFileWatcher on the form Drop_Email.

But the object isn't accessible either via its class Form_Drop_Email or through the [Forms] collection?

"In complete darkness we are all the same, it is only our knowledge and wisdom that separates us, don't let your eyes deceive you."

"If a shortcut was meant to be easy, it wouldn't be a shortcut, it would be the way!"

Free Dance Music Downloads
 
I guess your Form_Drop_Email isn't loaded at that time.
Can't you externalise this class and make it independent of the form?

“Knowledge is power. Information is liberating. Education is the premise of progress, in every society, in every family.” (Kofi Annan)
Oppose SOPA, PIPA, ACTA; measures to curb freedom of information under whatever name whatsoever.
 
I've gone back to having the oFileWatcher object on the main (parent) form.

If Forms!Check_Case.Form!Nav_Subform.Form.oFileWatcher is not nothing then the Drop_Email form HAS to be loaded as that's the only place in the entire application oFileWatcher is declared and instantiated.

Access is being weird with subforms and I can't work out why!

Thanks,
1DMF

"In complete darkness we are all the same, it is only our knowledge and wisdom that separates us, don't let your eyes deceive you."

"If a shortcut was meant to be easy, it wouldn't be a shortcut, it would be the way!"

Free Dance Music Downloads
 
It seems that the concept you are unfamiliar with is Scope.

If you look at procedure and variable declaration definitions and understand the keywords, Private, Public, Dim, Global you'll be well on your way. Keep in mind that each form has its own class module even if a subform is on a main form, that is two different class modules.
 
but it seems the class of a sub form is not directly accessible
Of course it is not accessible this way. No properties or methods can be referenced by the name of the class . You can never reference any object by its class name (Access magic that makes it look like you can, because it instantiates a form or report behind the scenes)(you can in vb.net where you have shared methods).

It seems that the concept you are unfamiliar with is Scope.
Also this has nothing to do with scope. It has to do with referencing instantiated objects, and not referencing by class name. Also some magic peculiar to Access form objects when referencing by class name.

Think about it. Imagine you built a form and put three of the same subforms on it. If you could reference by name, which one would be returned? That is why the only way to get a reference to it is through the control that contains it.
When a form is instantiated a pointer to that form is added to the forms collection. No reference is ever added for forms instantiated as subforms. So you cannot get it out of the forms collection either. Again think about. What if you had the form loaded three times as a subform? What name would you give each one?

Another thing is you need to remember is that in the project explorer
Form_YourFormName
Is the name of the form's class. That is not the name of an instantiated object. So what if you had a custom class "MyCustomClass". You would never be able to reference a property or method by the class name
MyCustomClass.oFileWatcher
You would have to instantiate an instance of my custom class
Same here with a form class

Now here is the magic, of why this even kind of sort of seems like it works. With access form's and reports when you reference a method or property by the class name, it actually instantiates an instance and loads this instance with the property visible set to false. This is why you can actually return property values from what appears to be a closed form. In truth it is open and invisible.
You can test this simply
Code:

If I did not make it visible it would have gone out of scope once the procedure ended.

So even if Form_Drop_Email was a form and not a subform, and you did this
form_Drop_Email.ofilewatcher
If Drop_Email was not already loaded this would cause instantiate and instance of form Drop_Email to load hidden, and then return a reference to that ofilewathcer.
If Drop_Email is already loaded then it will not load another instance and refer to the loaded instance and return oFileWatcher.

If you have form_Drop_Email instantiated as a subform and you do
form_Drop_Email.ofilewatcher
It does not know that there is a instance instantiated because it is a subform. Again this makes sense. So it opens a new instance of Form_Drop_Email hidden and returns a reference to that forms ofilewathcer. Not the one you thought.
 
Sorry here is the simple test
Code:
Public Sub Instantiate()
  Dim frm As Access.Form
  Form_FrmInstanciation.Visible = True
  Form_subFormOrders.Visible = True
  For Each frm In Forms
    Debug.Print frm.Name
  Next
End Sub
 
Here is a better demo of what happens.
I have a form called FrmParentWithOrdersSubForm
It has two subform controls with the same form inside "subformOrders"
The first subform control is called subFrmCtlOrders
The second is child2

I open the main form, and show the caption of the first subform. Then I change that subforms caption.
Then I reference the caption by the subforms class name: Form_subFormOrders.Caption
This opens a hidden instance of the form "subFormOrders" and you will see its caption is the unmodified caption.
Also you see that the second subforms caption never changes.
Also you see only the mainform instance is added to the Forms collection.
Code:
Public Sub BetterDemo()
  Dim mainForm As Form
  Dim frm As Access.Form
  DoCmd.OpenForm "FrmParentWithOrdersSubForm"
  Set mainForm = Forms("FrmParentWithOrdersSubForm")
  Debug.Print mainForm.subFrmCtlOrders.Form.Name
  Debug.Print mainForm.subFrmCtlOrders.Form.Caption
  mainForm.subFrmCtlOrders.Form.Caption = "ChangedCaption"
  Debug.Print mainForm.subFrmCtlOrders.Form.Caption
  Debug.Print mainForm.Child2.Form.Caption & " child2"
  Debug.Print Form_subFormOrders.Caption
  For Each frm In Forms
    Debug.Print frm.Name
  Next
End Sub

results
Code:
subFormOrders
OriginalCaption
ChangedCaption
OriginalCaption child2
OriginalCaption
FrmParentWithOrdersSubForm
 
Having reread the Op's 2nd post a few times...

I can add/expound a bit or perhaps put it another way...

"Open" MAIN Forms are maintained in a Forms collection hanging off the application object (Forms!Formname). Subforms exist in a special control on a PARENT Form (which can be a Main form or sub form - there is a limit I belive 2 subforms deep).

Forms you can reference in the forms collection are open or instantiated main forms or in that Forms collection. Forms and Reports definitions also exist in the DOCUMENTS collection. Access maintains that only one Main Form of a particular form definition can be open at once (AFAIK) and uses the definition name as the reference (subtle but what must be technically happening).

An important note about referencing subforms, you reference the control name of the sub form and get it's form property/object. Access DEFAULTS the control name for subforms to the name of the form. This is NOT the forms collection and does NOT have the on instance limit.

So, "Form_Drop_Email" is not in the forms collection as it is not a Main form and apparently Nav_Subform is the name of the control instaniating a "Form_Drop_Email" based on what works.

Since only instantiated objects are in scope... You would need your object declared somewhere to be in scope - like maybe not in the form scope which of course is key here.

Something to know, when code crashes / errors all variable values are volatile and die. That is why the TempVars collection exists as an alternative to globals. If you use an older version of Access that doesn't support it, the alternative to Tempvars would be a hidden unbound form where you update the values.
 
Access maintains that only one Main Form of a particular form definition can be open at once (AFAIK) and uses the definition name as the reference (subtle but what must be technically happening).
No. You can have as many instances of a form that you want. It is a class like any other class. The docmd.openform method limits it to only one instance.

Code:
dim frmOne as New Form_someForm
dim frmTwo as New Form_someForm
dim frmThree as New Form_someForm
frmOne.visible = true
frmTwo.visible = true
frmThree.visible = true
....

That will create three separate visible instances of the same form. Yes a pointer is only added to the forms collection for the first instantiated form. And yes they all share the same name property. Therefore you have to roll your own collection to manage multiple form instances.

So, "Form_Drop_Email" is not in the forms collection as it is not a Main form
I hate to get into semantics, but that is just a confusing explanation. "Form_Drop_Email" is the name of the class. Probably what you mean to say is that a pointer to an instance of this class is not added to the collection if the instance is instantiated only in a subform.

Since only instantiated objects are in scope... You would need your object declared somewhere to be in scope - like maybe not in the form scope which of course is key here
Again scope is definitely not the key here. Any form that is instantiated can be accessed through the forms collection thus global in scope. Any form instantiated in a subform can be accessed through the control of the form in the forms collection, thus global in scope. I have already shown that form and report instances are automatically instantiated when calling a property or method of the class. This is unique to Access objects. So not really sure what you are saying about scope, because I have proven that an instance of the form is automatically instantiated.
 
>This is unique to Access objects

Not quite. It applies to any class whose instancing has been defined as GlobalMultiUse (or GlobalSingleUse)
 
Wow, detailed info, but a bit overwhelming.

Are you saying as I am accessing objects via 'class name' (Form_MyForm), it is fine as long as it is an already open main form otherwise I will get a new instantiated object of that class?

I am aware of the use of access modifiers, scope and the volatile nature of objects when access crashes, I'm not sure I understand the point being made regarding this.

Also I have found other issues even when I finally get the syntax right to access the object via the main (parent) from in the forms collection...

thread702-1718948

It appears I am unable to use the object when on a subform, do you know why this is?

"In complete darkness we are all the same, it is only our knowledge and wisdom that separates us, don't let your eyes deceive you."

"If a shortcut was meant to be easy, it wouldn't be a shortcut, it would be the way!"

Free Dance Music Downloads
 
Are you saying as I am accessing objects via 'class name' (Form_MyForm), it is fine as long as it is an already open main form otherwise I will get a new instantiated object of that class.

As a coding practice I would not, because that is giving up control, things are happening automatically behind the scenes, and could cause you problems and confusion.

Imagine you develop a program, you open a form check some value and then do something. You check this using the method you describe. You test your app and it works fine. You do some modification where that form is closed before it gets to your line of code. If you would use the forms collection to get an explicit reference to a form, at design time you would have received a clear error stating that the object does not exist, and you would know clearly how to troubleshoot and fix. You need to open the form before your check or you need to verify it is loaded and do/not do something.

But if you reference using your method, it will open the form (hidden) and fire off the on open and on load events (without you seeing or really knowing). Probably not what you wanted to happen. So it may falsely appears to work, or work in some confusing way.

Your subform example is perfect reason for why you want to explicitly reference your instances and not do what you are proposing. Did you really want to open a hidden instance of your form? No. Was this difficult to troubleshoot? Yes. You can see that other experienced programmers, do not even understand this behavior. This falls into the category of things like multi value fields, or table lookups. If you fully understand these things and their implications then use them. However, for most people they do not understand what is going on behind the scenes and these usually cause more problems then savings.
 
Thanks MajP,

In my current situation, I know the form is open and always will be , it's the main form where all this functionality is applied.

If the main form wasn't open, then none of this would work, as nothing else would be triggering the watcher code.

However, I see your point and appreciate you clarifying how what I am doing works, as you say it does seem confusing at first if you don't understand how this works, hence me wondering why classname.object failed, yet formscollection...object worked.

Actually, you are correct that I was lucky that it errored in the first place with 'a null pointer', the only reason being is the folder watcher object isn't instantly instantiated when the form opens, there is a 500 millisecond timer event due to having to wait for the browser object to initialise before I can set the file path and start watching it!

So as the FileWatcher object hadn't instantiated yet, Form_Drop_Email.oFileWatcher was nothing, very lucky indeed, because if it hadn't , there wouldn't have been an error and I would be wondering why when I was accessing what I thought was the oFileWatcher object in the subform, was really accessing a newly instantiated oFileWatcher object from the hidden Drop_Email form object which wouldn't contain the same attribute values (state)

The irony that I couldn't use it as a subform object anyway is besides the point!



"In complete darkness we are all the same, it is only our knowledge and wisdom that separates us, don't let your eyes deceive you."

"If a shortcut was meant to be easy, it wouldn't be a shortcut, it would be the way!"

Free Dance Music Downloads
 
It applies to any class whose instancing has been defined as GlobalMultiUse (or GlobalSingleUse)
That is true for (VB), but As far as I know in VBA you only can instance as
Private
Public non creatable.
 
That will create three separate visible instances of the same form. Yes a pointer is only added to the forms collection for the first instantiated form.

So if I follow, it seems then that when a form is instantiated as a Main Form, it is added to the forms collection unless a form of the same class is already open. I believe that just didn't expect it. So then the next question I would have is what happens when you close frmOne... Is it removed from the forms collection, even though there are two forms of the same class, can then another be opened with docmd.openform? Will the next new class instance be added to the forms collection?

I'll probably find time to play later if noone else does.

 
This will answer your questions. I misspoke. All instances get added to the forms collection. The problem is that all instances share the same name, so normally you need to create your own collection to manage multiple form instances. The docmd.close will close the first instance of the object in the forms collection. The docmd open form will create a new form instance, if the other forms were instantiated without using docmd.open form. I do not really understand that. Not sure how it knows how the form was instantiated. But the example shows both cases.

To test this build a form called frmOne.

Code:
 Public frmA As Form_frmOne
 Public frmB As Form_frmOne
Public Sub TestForms()
  'You have to define your form variables at the module level
  'or they will go out of scope even if made visible
  Dim frm As Access.Form
  Set frmA = New Form_frmOne
  frmA.Visible = True
  frmA.Caption = "FrmA"
  Debug.Print "FrmA is Opened"
  Debug.Print "Objects in Form collection"
  For Each frm In Forms
   Debug.Print frm.Name & " " & frm.Caption
  Next frm
  Set frmB = New Form_frmOne
  frmB.Visible = True
  Debug.Print "Frm B is opened"
  frmB.Caption = "FrmB"
  Debug.Print "Objects in Form collection"
  For Each frm In Forms
    Debug.Print frm.Name & " " & frm.Caption
  Next frm
  Debug.Print "close it by Name closes out first instance in collection"
  DoCmd.Close acForm, "frmOne"
  Debug.Print "Objects in Form collection"
  For Each frm In Forms
    Debug.Print frm.Name & " " & frm.Caption
  Next frm
  DoCmd.OpenForm "frmone"
  Debug.Print "Open an instance by name"
  Debug.Print "Objects in Form collection"
  For Each frm In Forms
    Debug.Print frm.Name & " " & frm.Caption
  Next frm
  Debug.Print "Try to open another by name"
  DoCmd.OpenForm "frmOne"
  Debug.Print "Objects in Form collection"
  For Each frm In Forms
    Debug.Print frm.Name & " " & frm.Caption
  Next frm
  Debug.Print "close it by Name closes out first instance in collection"
  DoCmd.Close acForm, "frmOne"
  Debug.Print "Objects in Form collection"
  For Each frm In Forms
    Debug.Print frm.Name & " " & frm.Caption
  Next frm
    Debug.Print "close it by Name closes out first instance in collection"
  DoCmd.Close acForm, "frmOne"
  Debug.Print "Objects in Form collection"
  For Each frm In Forms
    Debug.Print frm.Name & " " & frm.Caption
  Next frm
End Sub
results
Code:
FrmA is Opened
Objects in Form collection
frmOne FrmA

Frm B is opened
Objects in Form collection
frmOne FrmA
frmOne FrmB

close it by Name closes out first instance in collection
Objects in Form collection
frmOne FrmB

Open an instance by name
Objects in Form collection
frmOne FrmB
frmone OriginalCaption

Try to open another by name
Objects in Form collection
frmOne FrmB
frmone OriginalCaption

close it by Name closes out first instance in collection
Objects in Form collection
frmone OriginalCaption

close it by Name closes out first instance in collection
Objects in Form collection
 
should read:
The docmd open form will create a new form instance, if the other forms were instantiated without using docmd.open form. It will not create a new form instance if there exists a form instantiated using the docmd.open form.

In other words
docmd.openform "frmOne"
docmd.openform "frmOne" 'will not create a second form

but
Dim frmA as New Form_frmOne
docmd.openForm "frmOne" 'will create a second form
 
>That is true for (VB), but As far as I know in VBA you only can instance as

Firstly, I was really just responding to your statement that this behaviour "is unique to Access objects". It isn't. We can create GlobalMultiUse classes in various COM-aware languages.
And secondly, in theory (if not in practice) is possible to create GlobalMultiUse classes in VBA, as the specification for VBA modules indicates ( It's just that none of the common VBA hosts allow us to create and host genuine library projects; typically they are source projects, and the VBA IDE is designed for such (you can manually edit cls files, for example, to enable GlobalMultiUse, but the IDE rests this when you reimport. On the other hand there is some missing functionality which IS supported - such as being able to add a default class method)
 
I see, what I wrote is definately not what I meant. I was trying to say
"In Access the only classes where you can reference the class properties/methods without instantiating an object are forms and reports, because you cannot build globalmultiuse classes"

Are you saying you can edit the cls file and it will become gmu? If so how? What gets modified or added? Or did you say the re-import would reset the values.
Code:
VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
END
Attribute VB_Name = "Widget"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top