0

I want to create class that is intended to serve as configuration management and serving helper. It would store some basic connection settings in XML file, read it to it's own properties and serve to any other module, form, object etc. I've created a class like following:

Option Compare Database
Option Explicit

Private mstrSqlServerName As String
'...


Public Property Get SqlServerName() As String
   SqlServerName = mstrSqlServerName
End Property
'...

Private Sub Class_Initialize()
    readConfigFile
End Sub

Private Function loadConfigFile() As MSXML2.DOMDocument60
On Error GoTo FunctionError
Dim XMLFileName As String
Dim objRoot As IXMLDOMElement
Dim strMsg As String
Dim oXMLFile As MSXML2.DOMDocument60
 
'Open the xml file
Set oXMLFile = New MSXML2.DOMDocument60
XMLFileName = (Application.CurrentProject.Path & "\config.xml")
With oXMLFile
    .validateOnParse = True
    .SetProperty "SelectionLanguage", "XPath"
    .async = False
    .Load (XMLFileName)
End With
    
Set loadConfigFile = oXMLFile

End Function


Public Sub readConfigFile()

    Dim oXMLFile As MSXML2.DOMDocument60
    
    Set oXMLFile = loadConfigFile

    mstrSqlServerName = oXMLFile.selectSingleNode("//config/database/SqlServerName").Text

End Sub

I've tested my class in intermediate window and everything works flawlessly. Then I've created hidden form to instantiate the class like follows:

'frmYouShouldNotSeeMe
Option Compare Database
Option Explicit

Public configuration As clsConfiguration

Private Sub Form_Load()

    Set configuration = New clsConfiguration
    MsgBox Prompt:=configuration.SqlServerName

End Sub

Now, from other module/form/report etc. i wanted to call configuration item with:

MsgBox configuration.SqlServerName

But when I'm trying to compile my project it says "compile error, variable not defined". In short: I cannot find a way to use instantiated (named) object properties from other objects of database. They just don't know anything about it's existence. In the intermediate window everything works, I can instantiate object and call GET functions. What did I do wrong? Probably it's just what OOP design brings and I just don't understand that. What is the best way to achieve this goal?

Calling configuration object from some other form in Access DB

ComputerVersteher
  • 2,638
  • 1
  • 10
  • 20
Smok
  • 101
  • 2
  • 11
  • Get rid of your remaining [hungarian notation](https://stackoverflow.com/questions/111933/why-shouldnt-i-use-hungarian-notation) (e.g `mstrSqlServerName`) and read [Private this As TSomething](https://rubberduckvba.wordpress.com/2018/04/25/private-this-as-tsomething/) for a beautiful pattern storing private instance fields, – ComputerVersteher Aug 20 '20 at 18:13

2 Answers2

2

You can add two public functions to your class.

Like this:

Public Function SqlServerName() As String


  SqlServerName = oXMLFile.SelectSingleNode("//config/database/SqlServerName").Text


End Function

Public Function GetConfig(strNode As String, strValue As String) As String

  Dim strXPATH  As String
  strXMPATH = "//config/" & strNode & "/" & strValue
  
  
  GetConfig = oXMLFile.SelectSingleNode(strXMPATH).Text


End Function

Now in code you can use:

 Msgbox configuration.SqlServerName

Or to get any config you can use the helper functon with 2 params

Msgbox configuration("database","SqlServerName")

So, the public members of the class HAVE to be written by you. It does not by "magic" or out of the blue expose information from that class. You can use public functions, and they will appear as inteli-sense when you create such public functions. (they become properties of the class). So, your syntax does not work because you don't have a public function (or a property get) that exposes what you want. You can use a property let/get, but a public function also works very well as the above shows.

So, either make a public function for each "thing" (node) you want to expose, or use the helper function above, and you can pass the key + node value with the function that accepts 2 parameters. Both the above will become part of the class, show inteli-sense during coding - you are free to add more public members to the class as per above.

#Edit

the sample code shows this:

Set configuration = New clsConfiguration
MsgBox Prompt:=configuration.SqlServerName

it needs to be this:

dim configuration as New clsConfiuration
MsgBox Prompt:=configuration.SqlServerName

You could on application startup setup a GLOBAL var called configueration, and thus not have to declare the configeration variable in the forms code.

You thus need a global var - set it up in your startup code before any form is launched.

So in a standard code module, you need this:

Public configuration as new clsConfiguration

But, your screen shot of the code is MISSING the create of the configuartion variable.

And your provided code has this:

Set configuration = New clsConfiguration
MsgBox Prompt:=configuration.SqlServerName

So where and when did you define configuration as global scoped variable?

You either have to use LOCAL scope to the form (in fact your on-load event)

dim configuration as New clsConfiuration
MsgBox Prompt:=configuration.SqlServerName

Or, you can as noted, declare "configuration" as a public global varible in a starndard code module (not forms module, and of course not a class module).

if you use the "new" keyword, then you can use this:

Public configuration as New clsConfiuration

The above can be placed in any standard code module - it will be global in scope.

With above, then in the forms load event, this will work:

 MsgBox Prompt:=configuration.SqlServerName
ComputerVersteher
  • 2,638
  • 1
  • 10
  • 20
Albert D. Kallal
  • 42,205
  • 3
  • 34
  • 51
  • Well, I did expect that this part will expose properly my properties: `Public Property Get SqlServerName() As String SqlServerName = mstrSqlServerName End Property` I'm a bit surprised that I need to provide any additional function for this purpose. In fact I was pretty sure, that my access db just cannot find my object because of visibility scope or inproper initialization... Especialy that if I manually initialize this class to an object in immediate window I can use it with no problem calling for example `?configuration.SqlServerName` – Smok Aug 19 '20 at 16:41
  • Hum, yes - I do (now) see the public SqlServerName (missed that). Yes, that should work. – Albert D. Kallal Aug 19 '20 at 18:33
  • 1
    Your code as posted does not compile - did you leave out the on error goto code part for loadConfigFile? The first line is On Error Goto FunctionError - yet that code stub is missing. – Albert D. Kallal Aug 19 '20 at 18:54
  • Also that screen shot does NOT show that you created an instance of the configuration object. You need right before that line of code dim configuration as new clsConfigueration. Or you can on application startup code initialize a global instance of that variable (as a class). – Albert D. Kallal Aug 19 '20 at 18:59
  • I've removed some lines of code for clarity. That's why the problems ocured. Objects Has Bern instantiated in other (startup) form. – Smok Aug 19 '20 at 19:04
  • see my edit - did not realize that the configuration var was NOT in scope - see my edits in my answer. – Albert D. Kallal Aug 19 '20 at 19:09
  • 1
    form scope vars are limited to the form - you need to place the Public var in a standard code module You can then initialize in that "other" form to global scope. But, with new keyword you don't even need the initialize on startup in that other form - it will occur on FIRST use with new keyword – Albert D. Kallal Aug 19 '20 at 19:11
  • Thank you. Now I do understand much more on scoping and forms/modules/classes differences. – Smok Aug 19 '20 at 20:11
  • There should be a hint on [auto assigned objects](https://rubberduckvba.com/Inspections/Details/SelfAssignedDeclaration) can't be checked for`Is Nothing` – ComputerVersteher Aug 20 '20 at 17:03
0

As configuration is declared in frmYouShouldNotSeeMe you can refer to the form to access the variable, as long as the form is open.

Debug.Print Forms("frmYouShouldNotSeeMe").configuration.SqlServerName
ComputerVersteher
  • 2,638
  • 1
  • 10
  • 20