Solved

Class Out of Scope Question

Posted on 2002-06-26
24
334 Views
Last Modified: 2013-11-23
I have created a collection in a class.  When my app starts up it runs through the class, reads an ini....assigns the ini data to a collection.  I also have a function in this class that is called outside of the class.  Then the rest of the app loads.  I have a gui on my app and when a click event is fired, the class.myFunction is called but my collection no longer contains data.  How do I get around this????

On my form (where the click event is fired) I have created a new instance of my class, then I call my function, then set the class instance to nothing.  I think I just have an empty class.

thanks,

knoxxx
0
Comment
Question by:JohnnyKnoxville
24 Comments
 
LVL 28

Expert Comment

by:AzraSound
ID: 7111996
>>I have created a new instance of my class

That answers your question.  If you need to reuse data within the first instance you used, you need to make it a public variable, e.g.,

Public clsMyClass As MyClass
0
 

Author Comment

by:JohnnyKnoxville
ID: 7112451
Well...I tried that, but it doesnt seem to be working.  

My collection is created in a class.  From my form I am now trying:

Public clsMyclass as New cCollectionClass


Before it was private...

Now when I try to use it:

for i = 0 to clsMyclass.myCollection.Count

next i

I receive the error object variable or with block not set.

I have the intellisense on the class now...but I am doing a watch on it and its empty.  ????

Any Ideas??

thanks again,,,

knoxxx
0
 
LVL 39

Expert Comment

by:appari
ID: 7112612
>>When my app starts up it runs through the class
what is your applications startup object? is it is sub main or a form? from where are you creating the first instance of the class?
put the following declaration in a bas module and try.
Public clsMyclass as New cCollectionClass
0
 

Author Comment

by:JohnnyKnoxville
ID: 7112769
I have a main app that creates an instance of the app that Im having difficulty with.  

When the app is created....it reads an INI file.  There I am assigning values to a collection. The collection is declared as follows:

Public colMyCollection as Collection

Then I have a loop where the values are assigned.  When I fall in the loop, thats where I set it to new.

Set colMyCollection = New Collection

All this occurs in a Public Function within the class.

When I run the main app....my app is created it is started by a Class_Initialize function in another class.  Which in turns spawn my other class that reads the INI file.  Once this is all complete, I set a breakpoint on a click event on a third party grid control.  When the grid is clicked I fall into the click procedure which calls my class that holds the value of the collection.  At the top of the form with the grid on it, I have this:

Public clsMyClass as New cINIClass

When I step through the code...I end up in the INIClass but the collection is empty.  Im totally confused.  How did I lose the scope?  Do I get a brand new class when I create an instance?  How does this work?  Is that why all of my variables are empty?


thanks,


knoxxx
0
 
LVL 10

Expert Comment

by:GoodJun
ID: 7113408
>Then I have a loop where the values are assigned.  When I fall in the loop, thats where I set it to new.

Set colMyCollection = New Collection

Did you put this inside the loop?

Change you class like this:
Public colMyCollection as new Collection
and remove the Set colMyCollection = New Collection from your loop.

0
 
LVL 28

Expert Comment

by:AzraSound
ID: 7113560
>>Set colMyCollection = New Collection

You destroy and create a new collection everytime this line is called.  Destroying meaning all data that was stored previously in the collection is lost.
0
 

Author Comment

by:JohnnyKnoxville
ID: 7114975
Ok....Im must be dumb cuz Im not even close to figuring this out.  Ive tried just about everything I can think of and so far...no luck.

Heres my entire class....

Private mvarsDesFile As String 'local copy
Public mvarRecipeEditor As frmRecipe 'local copy
Public Fields As New Collection
Public Headers As New Collection
Public Links As New Collection
Public colParms As Collection
Public colStepType As Collection
Private mvarsDespath As String 'local copy

Public Property Let sDesPath(ByVal vData As String)
    mvarsDespath = vData
End Property


Public Property Get sDesPath() As String
    sDesPath = mvarsDespath
End Property


Public Sub setForm(ByVal vData As Object)
    Set mvarRecipeEditor = vData
End Sub

Public Function bLoadDesFile() As Boolean
Dim sLine As String
Dim field As cRcpField
Dim i As Integer
Dim j As Integer
Dim sStepType As String
Dim sDisabledParms As String
Dim skey As String
Dim header As cRcpHeader
Dim link As cRcpLink


    If Dir$(mvarsDespath & mvarsDesFile) <> "" Then
        With mvarRecipeEditor
            .vsAwk1.filename = mvarsDespath & mvarsDesFile
            .vsAwk1.FS = "="
            .vsAwk1.Action = acNextLine
            .vsAwk2.FS = ":"
            skey = UCase(Trim(.vsAwk1.F(1)))
            If skey = "HEADER" Then
                Dim sName As String
                Dim nLine As Integer
                nLine = 1
                sName = skey
                Do
                    If sName <> "" Then
                        Set header = New cRcpHeader
                        header.Name = sName
                        header.Control = UCase(Trim(.vsAwk1.F(2)))
                        header.Line = nLine
                        Headers.Add header, UCase(Trim(sName))
                        nLine = nLine + 1
                    End If
                    .vsAwk1.Action = acNextLine
                    sName = Trim(.vsAwk1.F(1))
                    skey = UCase(sName)
                Loop While skey <> "COLUMN" And skey <> "LINK"
            Else
                Set header = New cRcpHeader
                header.Name = "EMPTY"
                Headers.Add header
            End If
           
            Dim nIndex As Integer
            nIndex = 1                  'The first data column in the Xarray is 1, column 0 is Step #
            While skey <> "EOF"
                If skey = "COLUMN" Then
                    Set field = New cRcpField
                    field.XRawColumn = nIndex
                    nIndex = nIndex + 1
                    .vsAwk1.Action = acNextLine
                    skey = UCase(Trim(.vsAwk1.F(1)))
                    Dim nFieldKey As String
                    While skey <> "EOF" And skey <> "COLUMN" And skey <> "DISABLED PARAMETERS"
                        Select Case skey
                        Case "NAME"
                            field.Key = .vsAwk1.F(2)
                            nFieldKey = UCase(Trim(field.Key))
                        Case "TYPE"
                            field.sType = UCase(Trim(.vsAwk1.F(2)))
                        Case "LIMIT"
                            .vsAwk2.F(0) = .vsAwk1.F(2)
                            For j = 1 To .vsAwk2.NF
                                field.Limits.Add UCase(Trim(.vsAwk2.F(j)))
                            Next j
                        Case "TRANSLATE"
                            .vsAwk2.F(0) = .vsAwk1.F(2)
                            For j = 1 To .vsAwk2.NF
                                field.translates.Add .vsAwk2.F(j)
                            Next j
                        Case "CONTROL"
                            .vsAwk2.F(0) = .vsAwk1.F(2)
                            For j = 1 To .vsAwk2.NF
                                field.Controls.Add UCase(Trim(.vsAwk2.F(j)))
                            Next j
                        Case "WIDTH"
                            field.Width = Val(.vsAwk1.F(2))
                        End Select
                        .vsAwk1.Action = acNextLine
                        skey = UCase(Trim(.vsAwk1.F(1)))
                    Wend
                    Fields.Add field, nFieldKey
                   
                End If
               
                If skey = "LINK" Then
                    Set link = New cRcpLink
                    .vsAwk1.Action = acNextLine
                    skey = UCase(Trim(.vsAwk1.F(1)))
                    Dim sLinkKey As String
                    While skey <> "EOF" And skey <> "LINK" And skey <> "COLUMN"
                   
                        'link.Name = skey
                        Select Case skey
                        Case "PATH"
                            link.Path = .vsAwk1.F(2)
                        Case "FILTER"
                            link.Filter = UCase(Trim(.vsAwk1.F(2)))
                        Case "CONTROL"
                            link.Control = UCase(Trim(.vsAwk1.F(2)))
                        Case "NAME"
                            link.Name = .vsAwk1.F(2)
                            sLinkKey = UCase(Trim(link.Name))
                        End Select
                        .vsAwk1.Action = acNextLine
                        skey = UCase(Trim(.vsAwk1.F(1)))
                    Wend
                    Links.Add link, sLinkKey
                End If
               
                If skey = "DISABLED PARAMETERS" Then
                    Set colParms = New Collection
                    Set colStepType = New Collection
                   
                    .vsAwk1.Action = acNextLine
                    skey = UCase(Trim(.vsAwk1.F(1)))
                    Dim sParmKey As String
                    While skey <> "EOF"
                      If skey <> "" Then
                        Select Case skey
                           Case "STEPTYPE"
                                .vsAwk2.F(0) = .vsAwk1.F(2)
                                sStepType = .vsAwk1.F(2)
                                colStepType.Add sStepType
                           Case "PARMS"
                                .vsAwk2.F(0) = .vsAwk1.F(2)
                                sDisabledParms = .vsAwk1.F(2)
                                colParms.Add sDisabledParms
                        End Select
                      End If
                      .vsAwk1.Action = acNextLine
                      skey = UCase(Trim(.vsAwk1.F(1)))
                    Wend
                End If
            Wend
            .vsAwk1.Action = acClose
        End With
    End If
   
End Function

Public Property Let sDesFile(ByVal vData As String)
    mvarsDesFile = vData
End Property


Public Property Get sDesFile() As String
    sDesFile = mvarsDesFile
End Property

Public Function DisableParms(ByVal sStep As String)
    Dim iCount As Integer
    Dim vType As Variant
    Dim sParmVal As String
    Dim sColVals() As String
    Set colStepType = New Collection
    Set colParms = New Collection
   
    sStep = UCase(Trim(sStep))
     
    For iCount = 1 To colStepType.Count
        vType = UCase(Trim(colStepType.Item(iCount)))
        If vType = sStep Then
            sParmVal = colParms.Item(iCount)
            sColVals() = Split(sParmVal, ":")
            frmRecipe.TDBGrid1.Columns(5).Locked = True
            Exit For
        End If
    Next iCount
                 
End Function

Im only concerned with the part where I check for 'Disabled Parameters'.  I read the INI through a third party control (vsAwk) which gives me line by line.  If I set a watch on the collections while they're being assigned, each collection looks like this:

colStepType
number1
number2
number3
number4

colParms
0:1:2:3:4:5
0:1:5:7:9:15
0:1:12:13:18:25
5:11:14:16:19:21

So....the function bLoadDesFile is called from another class that has this at the top of it...


Private mvarRcpDescr as new cRcpDescription

If I try and change this to Public...I get this error when I try to run it...

'Compile Error -- Private Object Modules cannot be used in Public Object Modules as parameters or return types for public procedures as public data members or as fields of public User defined types.'

I dont think this is my problem though.  

Here is the form where I make the call to this class...


Private m_fEnumPad As frmEnumPad
Const INTFMT = "##0"
Const DBLFMT = "#0.0#"
Const nROWHEIGHT = 500
Private nLowLimit As Double
Private nHighLimit As Double
Private bDontVerify As Boolean


Public Property Get Dirty() As Boolean

   Dirty = m_bDirty
   
End Property

' Method:
' Author:
' Date:
' Purpose:

Public Property Let Dirty(bDirty As Boolean)

   m_bDirty = bDirty

End Property

' Method:
' Author:
' Date:
' Purpose:

Private Sub Form_Load()

   'Set m_fNumPad = New frmNumPad
   Set m_fEnumPad = New frmEnumPad
   Set EditConfigs = New Collection
   Dim sPattern As String
   Dim S As New TrueDBGrid50.Style
   S.Font.Bold = True
   S.ForeColor = vbBlue
   S.Font.Size = 11
   sPattern = "Condition"
   TDBGrid1.AddRegexCellStyle dbgNormalCell, S, sPattern
   
   Set S = New TrueDBGrid50.Style
   sPattern = ".*" & vbCrLf
   S.Font.Bold = True
   S.BackColor = vbBlue
   S.Font.Size = 11
   TDBGrid1.AddRegexCellStyle dbgNormalCell, S, sPattern
   
   TDBGrid1.RowHeight = nROWHEIGHT
   TDBGrid1.HeadingStyle.WrapText = True

End Sub

' Method:
' Author:
' Date:
' Purpose:

Private Sub Form_Resize()

   TDBGrid1.Height = Me.Height

End Sub

' Method:
' Author:
' Date:
' Purpose:

Private Sub Form_Unload(Cancel As Integer)

   Set EditConfigs = Nothing
   'Unload m_fNumPad
   'Set m_fNumPad = Nothing
   Unload m_fEnumPad
   Set m_fEnumPad = Nothing

End Sub


Public Sub TDBGrid1_RowColChange(LastRow As Variant, ByVal LastCol As Integer)
     
     Set clsRcpDescr = New cRcpDescription
     clsRcpDescr.DisableParms (TDBGrid1.Columns(19).Text)
     Set clsRcpDescr = Nothing
End Sub

This last procedure is where I am calling the function from the class.  When I step through it and step into the class....the collection is empty.

The only thing I can think of is that when the class initially reads the INI file...it is reading five other INI files.  Only the first two have the text 'Disabled Parameters' in it.  Will this cause my collection to be emptied for some reason?

Thank you sooo much for the help...sorry to put so much in this question but this way you have all of the code.

knoxxx.

0
 

Author Comment

by:JohnnyKnoxville
ID: 7114977
OOps....this is also on top of the form


Private clsRcpDescr As cRcpDescription


thanks,


knoxx
0
 
LVL 28

Expert Comment

by:AzraSound
ID: 7114999
In your class, each time you have written:

Set colParms = New Collection
Set colStepType = New collection

you destroy anything you have ever put into those collections.  All data will be lost.  If you need to retain info in those collections for later use, remove all of those "Set col* = New" statements and move it to the Class_Initialize event.  In the Class_Terminate event, set them to nothing.


Now in your form code, you have this:

Public Sub TDBGrid1_RowColChange(LastRow As Variant, ByVal LastCol As Integer)
   
    Set clsRcpDescr = New cRcpDescription
    clsRcpDescr.DisableParms (TDBGrid1.Columns(19).Text)
    Set clsRcpDescr = Nothing
End Sub


Lets look at what happens after certain line calls...

***Set clsRcpDescr = New cRcpDescription

This destroys the class, calling its Terminate event, then calling its Initialize event (since its creating him all over again)

Thus, if you move the collection statements as I suggested, you would first destroy the collections, then recreate them.

***clsRcpDescr.DisableParms (TDBGrid1.Columns(19).Text)
Now you do the functionality of the procedure, etc.


***Set clsRcpDescr = Nothing
Now you destroy the class, calling its Terminate event, and thus destroying your collections.  If you need to access this class AT ALL outside if this grid's RowColChange event, you need to alter that code.  Do you really need to destroy and recreate the class everytime this event fires?   Can you just create the class in the Form_Load event and destroy it in the Form_Unload event??
0
 

Author Comment

by:JohnnyKnoxville
ID: 7115133
Ok...Ive moved the set collection statements as suggested.  Do I need to remove the clsRcpDescr = New cRcpDescription?  If I do...I get a object variable or with block variable not set error.  So I put it back and stepped through it...

On my function call I stepped right into the class initialize of my class (which now sets my new collections). It doesnt look like I want to do this.  How can I call the function from the form without setting clsRcpDescr = New cRcpDescription????  


Man....thanks a million for the help.  Im learning as fast as I can. :)


knoxxx
0
 
LVL 28

Expert Comment

by:AzraSound
ID: 7115146
Move the "Set clsRcpDescr = New cRcpDescription" to your Form_Load event and the "Set clsRcpDescr = Nothing" to your Form_Unload event.
0
 

Author Comment

by:JohnnyKnoxville
ID: 7115180
Ok...Ive moved the set collection statements as suggested.  Do I need to remove the clsRcpDescr = New cRcpDescription?  If I do...I get a object variable or with block variable not set error.  So I put it back and stepped through it...

On my function call I stepped right into the class initialize of my class (which now sets my new collections). It doesnt look like I want to do this.  How can I call the function from the form without setting clsRcpDescr = New cRcpDescription????  


Man....thanks a million for the help.  Im learning as fast as I can. :)


knoxxx
0
6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

 

Author Comment

by:JohnnyKnoxville
ID: 7115199
Well...I dont get any errors...but I do a watch on colStepType.count as its value is 0????


Any thoughts on this.  Sorry to burden you with my problem.  Thanks so much for the quick responses.



knoxx
0
 

Author Comment

by:JohnnyKnoxville
ID: 7115232
Well...I dont get any errors...but I do a watch on colStepType.count as its value is 0????


Any thoughts on this.  Sorry to burden you with my problem.  Thanks so much for the quick responses.



knoxx
0
 

Author Comment

by:JohnnyKnoxville
ID: 7115248
Ok...I set breakpoints on the class_Initialize where my set myColl = new Collection are at.  This is getting hit left and right.  So....I think thats why its empty.  How do I get around this?????  Should I create a class that stores the collections there?  That way nothing is calling the class initialize.  When my form is called during startup i step into the form_load event alot which calls set clsRcpDescr = new cRcpDescription  which in turn fires the Class_Initialize.  When you set the class to new in anther class...it calls the Class_Initialize???  I think Im screwed.


thanks

knoxxx
0
 

Author Comment

by:JohnnyKnoxville
ID: 7115292
Ok...I set breakpoints on the class_Initialize where my set myColl = new Collection are at.  This is getting hit left and right.  So....I think thats why its empty.  How do I get around this?????  Should I create a class that stores the collections there?  That way nothing is calling the class initialize.  When my form is called during startup i step into the form_load event alot which calls set clsRcpDescr = new cRcpDescription  which in turn fires the Class_Initialize.  When you set the class to new in anther class...it calls the Class_Initialize???  I think Im screwed.


thanks

knoxxx
0
 

Author Comment

by:JohnnyKnoxville
ID: 7115314
Ok...I set breakpoints on the class_Initialize where my set myColl = new Collection are at.  This is getting hit left and right.  So....I think thats why its empty.  How do I get around this?????  Should I create a class that stores the collections there?  That way nothing is calling the class initialize.  When my form is called during startup i step into the form_load event alot which calls set clsRcpDescr = new cRcpDescription  which in turn fires the Class_Initialize.  When you set the class to new in anther class...it calls the Class_Initialize???  I think Im screwed.


thanks

knoxxx
0
 

Author Comment

by:JohnnyKnoxville
ID: 7115323
Ok...I set breakpoints on the class_Initialize where my set myColl = new Collection are at.  This is getting hit left and right.  So....I think thats why its empty.  How do I get around this?????  Should I create a class that stores the collections there?  That way nothing is calling the class initialize.  When my form is called during startup i step into the form_load event alot which calls set clsRcpDescr = new cRcpDescription  which in turn fires the Class_Initialize.  When you set the class to new in anther class...it calls the Class_Initialize???  I think Im screwed.


thanks

knoxxx
0
 

Author Comment

by:JohnnyKnoxville
ID: 7115344
Ok...I set breakpoints on the class_Initialize where my set myColl = new Collection are at.  This is getting hit left and right.  So....I think thats why its empty.  How do I get around this?????  Should I create a class that stores the collections there?  That way nothing is calling the class initialize.  When my form is called during startup i step into the form_load event alot which calls set clsRcpDescr = new cRcpDescription  which in turn fires the Class_Initialize.  When you set the class to new in anther class...it calls the Class_Initialize???  I think Im screwed.


thanks

knoxxx
0
 
LVL 28

Expert Comment

by:AzraSound
ID: 7115509
Yes each time you set a class to new you will fire its Initialize event.  Where else are you setting this class to new other than in your Form_Load event?
0
 

Author Comment

by:JohnnyKnoxville
ID: 7115668
The existing code is calling this class in various locations.  However, There is another class that is referencing this class and doesnt have a problem retreiving the data...

There is another class (cSubRcp)...that creates an instance of the INI class.  

private mvarcRcpDesp as new cRcpDescription

Then in some functions, it does this:

mvarcRcpDesp.Headers.Count

and the value is returned.  Why can it pull the data from Headers and not the Parms collection.

The only difference that I can see is this:


I am trying to access it from the form and not this class.

I tried to create an instance of this subRcp class instead ...putting the DisableParm function there.  But then when I do this:
Private cRcpSub as New cSubRcp

then this where the event is fired.
cRcpSub.DisableParms (blah)

While the program is loading...I get stack and memory errors.

Im running into all kinds of problems like this...

Im about to call it quits cuz this really sucks.

knoxxx
0
 
LVL 28

Accepted Solution

by:
AzraSound earned 100 total points
ID: 7117062
I dont mean to be offensive here, but your overall architecture needs to be looked at.  In your class I see you make a direct reference to a particular form.  The idea of a class is that it provides general functionality such that it can be reused in other applications.  It should also exist such that it doesnt need to know anything about an external source that uses it.  Both rules are broken when you reference a form directly in the class' code.

The problems you are having are probably quite trivial, but I have gotten quite lost in the discussion and the length of this thread.  If you have done as I suggested, and removed the "Set colParams = New collection" out of both functions in your class that do so, and placed it, instead, in the Class_Initialize event, then you should not have problems retaining information in that collection unless you are destroying the class object itself in code (since we destroy the collection ONLY in the Class_Terminate event).  How can you destroy the class object?  A couple scenarios:



Function SomeFunction()
    Dim c As myClass

    'when the function ends, the class terminates
End Function




Option Explicit
Public c As myClass

'if you have the above in a form, each time you Load/Unload the form, you create/destroy that class



I'm not sure why else you would be losing the information in your collection.  Again, it may be something related to your general program design, and it would require that I actually get into your project and look at how it is structured and functions.
0
 

Author Comment

by:JohnnyKnoxville
ID: 7122760
I really cant thank you enough for all of your help.  Most of the code is existing and I'm now looking at breaking it apart to allow for a more fluid programming structure.  Thank you for the lesson(s) in variable and class scope.  I really appreciate it.


Knoxxx
0
 
LVL 28

Expert Comment

by:AzraSound
ID: 7122830
Glad I could help   :-)
0

Featured Post

Threat Intelligence Starter Resources

Integrating threat intelligence can be challenging, and not all companies are ready. These resources can help you build awareness and prepare for defense.

Join & Write a Comment

Most everyone who has done any programming in VB6 knows that you can do something in code like Debug.Print MyVar and that when the program runs from the IDE, the value of MyVar will be displayed in the Immediate Window. Less well known is Debug.Asse…
You can of course define an array to hold data that is of a particular type like an array of Strings to hold customer names or an array of Doubles to hold customer sales, but what do you do if you want to coordinate that data? This article describes…
Get people started with the process of using Access VBA to control Outlook using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Microsoft Outlook. Using automation, an Access applic…
Get people started with the utilization of class modules. Class modules can be a powerful tool in Microsoft Access. They allow you to create self-contained objects that encapsulate functionality. They can easily hide the complexity of a process from…

758 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

Need Help in Real-Time?

Connect with top rated Experts

22 Experts available now in Live!

Get 1:1 Help Now