Just about every program ends up with some global variables when it gets large enough, and managing them is just one of those things which you have to take into account as you continue extending or bug fixing the program. Being someone who dislikes having to constantly run into situations where "Oh no, I need another global!", I started using a Dictionary Object with VB programs. That was fairly simple, the object didn't care what type of value you passed to it, and VB didn't care what it got from it. On the surface that is, I know there was a lot of type casting going on in the background in the compiled program, didn't care.

In "porting" one of my programs from VB to PB, I really needed to maintain some use of that ability to manage all my configuration settings in a single place so it was not only easy to access and use, but to also be able to export and import the settings. Really critical stuff when you want to be able to permit the user of the program to adjust things to their taste, and not have to do it each time they start the program.

So, I wrote a class which would fufill that need:
Code:
' class Configuration Manager

%INSERTCONSTANTS%

CLASS ConfigurationClass
 %INSERTINSTANCES%

 CLASS METHOD CREATE()
 END METHOD
 CLASS METHOD DESTROY()
 END METHOD

 INTERFACE ConfigurationHandler
  INHERIT IUNKNOWN

  PROPERTY GET cString(BYVAL ItemIndex AS LONG) AS STRING
   LOCAL rString   AS STRING
   IF ItemIndex <= %cfgMax THEN
    SELECT CASE ItemIndex
     %INSERTGETSTRING%
    END SELECT
   END IF
   PROPERTY = rString
  END PROPERTY

  PROPERTY SET cString(BYVAL ItemIndex AS LONG, BYVAL ItemValue AS STRING)
   IF ItemIndex <= %cfgMax THEN
    SELECT CASE ItemIndex
     %INSERTSETSTRING%
    END SELECT
   END IF
  END PROPERTY

  PROPERTY GET cTitle(BYVAL ItemIndex AS LONG) AS STRING
   LOCAL rString   AS STRING
   IF ItemIndex <= %cfgMax THEN
    SELECT CASE ItemIndex
     %INSERTGETTITLE%
    END SELECT
   END IF
   PROPERTY = rString
  END PROPERTY

  PROPERTY GET cValue(BYVAL ItemIndex AS LONG) AS LONG
   LOCAL rValue AS LONG
   IF ItemIndex <= %cfgMax THEN
    SELECT CASE ItemIndex
     %INSERTGETVALUES%
    END SELECT
   END IF
   PROPERTY = rValue
  END PROPERTY

  PROPERTY SET cValue(BYVAL ItemIndex AS LONG, BYVAL ItemValue AS LONG)
   IF ItemIndex <= %cfgMax THEN
    SELECT CASE ItemIndex
     %INSERTSETVALUES%
    END SELECT
   END IF
  END PROPERTY

  PROPERTY GET cBoolean(BYVAL ItemIndex AS LONG) AS LONG
   LOCAL rBoolean  AS LONG
   IF ItemIndex <= %cfgMax THEN
    SELECT CASE ItemIndex
     %INSERTGETBOOLEAN%
    END SELECT
   END IF
   PROPERTY = ISTRUE rBoolean
  END PROPERTY

  PROPERTY SET cBoolean(BYVAL ItemIndex AS LONG, BYVAL ItemValue AS LONG)
   IF ItemIndex <= %cfgMax THEN
    ItemValue = ISTRUE ItemValue
    SELECT CASE ItemIndex
     %INSERTSETBOOLEAN%
    END SELECT
   END IF
  END PROPERTY

  PROPERTY GET cName(BYVAL ItemIndex AS LONG) AS STRING
   LOCAL rName  AS STRING
   IF ItemIndex <= %cfgMax THEN
    SELECT CASE ItemIndex
     %INSERTGETNAME%
    END SELECT
   END IF
   PROPERTY = rName
  END PROPERTY

  PROPERTY GET cIndex(BYVAL ItemName AS STRING) AS LONG
   LOCAL rIndex AS LONG
   SELECT CASE UCASE$(ItemName)
    %INSERTGETINDEX%
    CASE ELSE
     rIndex = -1
   END SELECT
   PROPERTY = rIndex
  END PROPERTY

  METHOD MakeConfigurationString(BYVAL ItemIndex AS LONG) AS STRING
   LOCAL cString   AS STRING
   cString = me.cName(ItemIndex) 
   IF LEN(cString) > 0 THEN
    cString = cString & "="
    SELECT CASE ItemIndex
      %INSERTCONSTRUCTORS%
    END SELECT
     END IF
   METHOD = cString
  END METHOD

  METHOD SetConfigurationValue(BYVAL cString AS STRING) AS LONG
   LOCAL ItemIndex  AS LONG
   LOCAL cValue  AS STRING
   LOCAL Success    AS LONG
   ItemIndex = me.cIndex(TRIM$(PARSE$(cString, "=", 1)))
   cValue = TRIM$(PARSE$(cString, "=", 2))
   Success = %TRUE
   SELECT CASE ItemIndex
    %INSERTDESTRUCTORS%
    CASE ELSE
     Success = %FALSE
   END SELECT
   METHOD = Success
  END METHOD

 END INTERFACE

END CLASS
That's my template, which is included in the attached zip file, it's read by the Excel VBA, also in the attached zip, and filled in to produce something which manages the various settings you need to manage your program's operation. Lets Go over all the various psuedo %variables% so you can see how I put all this together.

%INSERTCONSTANTS%
Code:
%cfgActiveFolder                   = 0
%cfgCloseGracefully                = 1
%cfgSaveOnExit                     = 2
%cfgScreenX                        = 3
%cfgScreenY                        = 4
%cfgUseLongNames                   = 5
%cfgWindowCount                    = 6
%cfgWorkingFolder                  = 7
%cfgMax                            = 7
The constants are part of being able to use the class easy, the whole idea was to make it easy to get and set values in the class without needing to remember what ID was which value, and to not use a string for lookup.


%INSERTINSTANCES%
Code:
    INSTANCE cfgActiveFolder            AS STRING
    INSTANCE cfgCloseGracefully         AS LONG ' Boolean
    INSTANCE cfgSaveOnExit              AS LONG ' Boolean
    INSTANCE cfgScreenX                 AS LONG
    INSTANCE cfgScreenY                 AS LONG
    INSTANCE cfgUseLongNames            AS LONG ' Boolean
    INSTANCE cfgWindowCount             AS LONG
    INSTANCE cfgWorkingFolder           AS STRING
The compiler is not complaining about variable names being the same as the constants, and overall, it makes readability of the class easy. The class is designed to handle 3 types of values, STRING, LONG, and BOOLEAN. Seeing as PB don't have an intristic boolean value, it's a tad faked with a long.


%INSERTGETSTRING% / %INSERTSETSTRING% (cString)
Code:
    SELECT CASE ItemIndex
     CASE %cfgActiveFolder
       rString = cfgActiveFolder
Code:
    SELECT CASE ItemIndex
     CASE %cfgActiveFolder
       cfgActiveFolder = ItemValue
These two sections are for managing strings in the class, I suppose if you wanted to access values as strings, you could also include that ability into the class, however, as I constructed it, if the value you wanted was not a string, then an empty string is returned. Could probably add error management into the class so it does something if the programmer does something funky like that.


%INSERTGETTITLE% (cTitle)
Code:
    SELECT CASE ItemIndex
     CASE %cfgWorkingFolder
       rString = "Select Default Working Folder"
Special purpose class item for when I throw up an Open File dialog or a Browse For Folder dialog I can get the title for the function from the class based on the value I'm permitting the user to adjust.


%INSERTGETVALUES% / %INSERTSETVALUES% (cValue)
Code:
    SELECT CASE ItemIndex
     CASE %cfgScreenX
       rValue = cfgScreenX
Code:
    SELECT CASE ItemIndex
     CASE %cfgScreenX
       cfgScreenX = ItemValue
Overall, there is no restriction on having a class like this limited to strictly Long's for values, at this point, I have no need to include other values than the three I was interesting in maintaining. It's not unfeasable to include structures into the class and use the class for managing something complex.


%INSERTGETBOOLEAN% / %INSERTSETBOOLEAN% (cBoolean)
Code:
    SELECT CASE ItemIndex
     CASE %cfgCloseGracefully
       rBoolean = cfgCloseGracefully
Code:
    SELECT CASE ItemIndex
     CASE %cfgCloseGracefully
       cfgCloseGracefully = ItemValue
You will note, from the above template, and when dealing with boolean values in the configuraton class, I explicitedly set the inbound value to being either true or false, and likewise, when returning the value from the class, by using the ISTRUE function.


%INSERTGETNAME% (cName)
Code:
    SELECT CASE ItemIndex
     CASE %cfgActiveFolder
       rName = "cfgActiveFolder"
%INSERTGETINDEX% (cIndex)
Code:
   SELECT CASE UCASE$(ItemName)
    CASE "CFGACTIVEFOLDER"
      rIndex = %cfgActiveFolder
I suppose these two properties could of been made private to the class, it's a waffle. But they're for the next part of this class. The first one is for fetching the name of the associated configuration variable and the second is for determining the index for that name.


%INSERTCONSTRUCTORS% (MakeConfigurationString)
Code:
    SELECT CASE ItemIndex
      CASE %cfgActiveFolder
        cString = cString & ME.cString(ItemIndex)
      CASE %cfgCloseGracefully
        cString = cString & IIF$(ISTRUE ME.cBoolean(ItemIndex), "True", "False")
      CASE %cfgScreenX
        cString = cString & FORMAT$(ME.cValue(ItemIndex))
%INSERTDESTRUCTORS% (SetConfigurationValue)
Code:
   SELECT CASE ItemIndex
    CASE %cfgActiveFolder
      ME.cString(ItemIndex) = cValue
    CASE %cfgCloseGracefully
      ME.cBoolean(ItemIndex) = IIF(ucase$(cValue) = "TRUE", %TRUE, %FALSE)
    CASE %cfgScreenX
      ME.cValue(ItemIndex) = VAL(cValue)
I suppose you could include the ability to load and save the configuration settings to a file as part of the class, I didn't include that due to the need to have "other" values written to the file, mainly a recent files list. Hence the reason that SetConfigurationValue returns either TRUE or FALSE from the operation, if FALSE, then the line from the file needs to be handled differently.
Attached Files