|
|
(One intermediate revision by the same user not shown) |
Line 4: |
Line 4: |
| The settings are saved in [[XML]] format, courtesy of [[Microsoft]]'s baffling and largely undocumented [[MSXML2]] [[ActiveX]] interface from the "Microsoft XML, v3.0" library (filename is c:\windows\system\MSXML3.DLL), although the classes are intended to be pluggable on both ends (you should be able to swap out the XML code for code that handles other formats, as well as being able to add "savers" for more data types). | | The settings are saved in [[XML]] format, courtesy of [[Microsoft]]'s baffling and largely undocumented [[MSXML2]] [[ActiveX]] interface from the "Microsoft XML, v3.0" library (filename is c:\windows\system\MSXML3.DLL), although the classes are intended to be pluggable on both ends (you should be able to swap out the XML code for code that handles other formats, as well as being able to add "savers" for more data types). |
| | | |
− | * The "*API" classes are [[abstract class]]es which define the basic Settings [[API]] without actually doing anything.
| |
− | * The "*XML" classes implement the API classes using [[MSXML2]] to load and save the data.
| |
| ==Bugs== | | ==Bugs== |
| Items deleted from the saver-list may still be saved; something inside the XML objects is remembering them. You have to destroy the main Setting object and rebuild it at save-time in order to prevent this from happening. Fortunately, this doesn't seem to slow anything down noticeably. | | Items deleted from the saver-list may still be saved; something inside the XML objects is remembering them. You have to destroy the main Setting object and rebuild it at save-time in order to prevent this from happening. Fortunately, this doesn't seem to slow anything down noticeably. |
| ==Code== | | ==Code== |
− | ===clsSettingAPI===
| + | * [[User:Woozle/VB/Settings manager/sample code|sample code]]: how to use the class collection |
− | This is an abstract class for translating between internal data and the string to actually save. It is implemented by [[#clsSettingText]] and [[#xtForm]].
| + | * [[User:Woozle/VB/Settings manager/source code|source code]]: the classes themselves |
− | <pre>
| |
− | Option Explicit
| |
− | | |
− | ' CLASS: clsSettingAPI
| |
− | ' PURPOSE: abstract class for saving a single item's settings
| |
− | Public Name As String
| |
− | Public Value As String
| |
− | Public Sub Save(iNode As clsSettingsNodeAPI)
| |
− | ' abstract
| |
− | End Sub
| |
− | Public Sub Load(iNode As clsSettingsNodeAPI)
| |
− | ' abstract
| |
− | End Sub
| |
− | </pre>
| |
− | ===clsSettingsAPI===
| |
− | <pre>
| |
− | Option Explicit
| |
− | | |
− | ' CLASS: clsSettingsAPI
| |
− | ' PURPOSE: abstract settings-saving class
| |
− | | |
− | Public Function Root() As clsSettingsNodeAPI
| |
− | ' ACTION: Return the root node
| |
− | ' USAGE: abstract; this is model code:
| |
− | Dim objRoot As clsSettingsNodeAPI
| |
− |
| |
− | Set objRoot = New clsSettingsNodeAPI
| |
− | objRoot.Init Me, Nothing
| |
− | Set Root = objRoot
| |
− | End Function
| |
− | Public Sub Load(iForce As Boolean)
| |
− | ' abstract
| |
− | End Sub
| |
− | Public Sub Save()
| |
− | ' abstract
| |
− | End Sub
| |
− | Private Sub Class_Initialize()
| |
− | ' abstract; sample code
| |
− | Me.Load False
| |
− | End Sub
| |
− | Private Sub Class_Terminate()
| |
− | ' abstract; sample code
| |
− | Me.Save
| |
− | End Sub
| |
− | </pre>
| |
− | ===clsSettingsNodeAPI===
| |
− | <pre>
| |
− | ' CLASS: clsSettingsNodeAPI
| |
− | ' PURPOSE: set of values for clsSettingsAPI
| |
− | ' USAGE: Abstract
| |
− | | |
− | Option Explicit
| |
− | Private objMain As clsSettingsAPI
| |
− | Private objParent As clsSettingsNodeAPI
| |
− | Friend Sub Init(iSettings As clsSettingsAPI, iParent As clsSettingsNodeAPI)
| |
− | Set objMain = iSettings
| |
− | Set objParent = iParent
| |
− | End Sub
| |
− | Public Property Get Name() As String
| |
− | ' abstract
| |
− | End Property
| |
− | Public Function Node(iName As String, iCreate As Boolean) As clsSettingsNodeAPI
| |
− | ' ACTION: Return the root node
| |
− | ' USAGE: abstract; this is model code:
| |
− | Dim objNode As clsSettingsNodeAPI
| |
− |
| |
− | Set objNode = New clsSettingsNodeAPI
| |
− | ' find subnode with name iName; initialize objNode with it (not Nothing)
| |
− | objNode.Init Me, Nothing
| |
− | Set Node = objNode
| |
− | End Function
| |
− | Public Function AddItem(iItem As clsSettingAPI) As clsSettingsNodeAPI
| |
− | ' ACTION: Add an item to the tree; return the item's node object
| |
− | ' abstract
| |
− | End Function
| |
− | Public Property Get ValueStored() As String
| |
− | ' ACTION: Returns the *stored* value (not the active value)
| |
− | ' abstract
| |
− | End Property
| |
− | Public Property Let ValueStored(iValue As String)
| |
− | ' ACTION: Sets the *stored* value (not the active value)
| |
− | ' abstract
| |
− | End Property
| |
− | Public Sub Load()
| |
− | ' abstract
| |
− | End Sub
| |
− | Public Sub Save()
| |
− | ' abstract
| |
− | End Sub
| |
− | Public Property Get ListNodes() As Scripting.Dictionary
| |
− | ' abstract
| |
− | End Property
| |
− | </pre>
| |
− | ===clsSettingsXML===
| |
− | <pre>
| |
− | Private strSection As String
| |
− | Private strAppName As String
| |
− | Private strFileSpec As String
| |
− | Private xmlDoc As MSXML2.DOMDocument
| |
− | Private objRoot As clsSettingsNodeXML
| |
− | Private isLoaded As Boolean
| |
− | Public Sub Init(Optional iSection As String = "", Optional iAppName As String = "")
| |
− | If iSection <> "" Then
| |
− | strSection = iSection
| |
− | End If
| |
− | If iAppName <> "" Then
| |
− | strAppName = iAppName
| |
− | End If
| |
− |
| |
− | strFileSpec = App.Path & "\" & App.Title & ".prefs.xml"
| |
− | isLoaded = False
| |
− | End Sub
| |
− | Public Function Root() As clsSettingsNodeAPI
| |
− | ' ACTION: Return the root node
| |
− | ' Dim objRootSet As clsSettingText
| |
− | Dim xmlRoot As MSXML2.IXMLDOMNode
| |
− |
| |
− | If objRoot Is Nothing Then
| |
− | ' Set objRootSet = New clsSettingText
| |
− | ' objRootSet.Init strSection, ""
| |
− | Set objRoot = New clsSettingsNodeXML
| |
− | With Me.GetXMLDoc
| |
− | If .childNodes.Length = 0 Then
| |
− | Stop ' this should never happen - GetXMLDoc is supposed to create the root node
| |
− | Else
| |
− | Set xmlRoot = .firstChild
| |
− | End If
| |
− | End With
| |
− | objRoot.InitRoot strSection, xmlRoot, Me
| |
− | End If
| |
− | Set Root = objRoot
| |
− | End Function
| |
− | Public Property Get FileSpec() As String
| |
− | FileSpec = strFileSpec
| |
− | End Property
| |
− | Public Property Let FileSpec(iSpec As String)
| |
− | strFileSpec = iSpec
| |
− | End Property
| |
− | Public Sub Load(iForce As Boolean)
| |
− | ' ACTION: Read settings data from file
| |
− | If iForce Or (Not Me.Loaded) Then
| |
− | Me.GetXMLDoc.Load Me.FileSpec
| |
− | ' Set xmlDoc = Nothing ' loading the document clears all nodes
| |
− | isLoaded = True
| |
− | End If
| |
− | End Sub
| |
− | Public Sub Save()
| |
− | ' Dim objNode As clsSettingsNodeXML
| |
− |
| |
− | ' Set objNode = Me.Root
| |
− | ' objNode.Save
| |
− | Me.GetXMLDoc.Save Me.FileSpec
| |
− | isLoaded = True
| |
− | End Sub
| |
− | Public Function Loaded() As Boolean
| |
− | Loaded = isLoaded
| |
− | End Function
| |
− | Friend Function GetXMLDoc() As MSXML2.DOMDocument
| |
− | Dim xmlRoot As MSXML2.IXMLDOMNode
| |
− |
| |
− | ' create document if needed
| |
− | If xmlDoc Is Nothing Then
| |
− | Set xmlDoc = New MSXML2.DOMDocument
| |
− | End If
| |
− |
| |
− | ' create root node if needed
| |
− | If xmlDoc.childNodes.Length = 0 Then
| |
− | With xmlDoc
| |
− | ' create the root node
| |
− | Set xmlRoot = .createNode(NODE_ELEMENT, strSection, "")
| |
− | .appendChild xmlRoot
| |
− | End With
| |
− | End If
| |
− | Set GetXMLDoc = xmlDoc
| |
− | End Function
| |
− | Private Sub Class_Initialize()
| |
− | strSection = "settings" ' default
| |
− | strAppName = App.EXEName
| |
− | End Sub
| |
− | Private Sub clsSettingsAPI_Load(iForce As Boolean)
| |
− | Me.Load iForce
| |
− | End Sub
| |
− | Private Function clsSettingsAPI_Root() As clsSettingsNodeAPI
| |
− | clsSettingsAPI_Root = Me.Root
| |
− | End Function
| |
− | Private Sub clsSettingsAPI_Save()
| |
− | Me.Save
| |
− | End Sub
| |
− | ===clsSettingsNodeXML===
| |
− | <pre>
| |
− | Option Explicit
| |
− | | |
− | Implements clsSettingsNodeAPI
| |
− | | |
− | Private strName As String
| |
− | Private objMain As clsSettingsXML
| |
− | Private objParent As clsSettingsNodeXML
| |
− | Public XMLNode As MSXML2.IXMLDOMNode
| |
− | Private objItem As clsSettingAPI
| |
− | Private lstNodes As Scripting.Dictionary ' list of sub-nodes
| |
− | Private lstXMLs As Scripting.Dictionary ' list of XML sub-nodes (refresh before using)
| |
− | Friend Sub Init(iItem As clsSettingAPI, iXML As MSXML2.IXMLDOMNode, iParent As clsSettingsNodeAPI)
| |
− | Set objItem = iItem
| |
− | Set objParent = iParent
| |
− | Me.InitRoot iItem.Name, iXML, objParent.Main
| |
− | End Sub
| |
− | Friend Sub InitRoot(iName As String, iXML As MSXML2.IXMLDOMNode, iMain As clsSettingsXML)
| |
− | ' USAGES: (1) Internally, and (2) for clsSettingsXML to initialize root node only
| |
− | strName = iName
| |
− | Set XMLNode = iXML
| |
− | Set objMain = iMain
| |
− | End Sub
| |
− | Friend Sub InitHolder(iName As String, iXML As MSXML2.IXMLDOMNode, iParent As clsSettingsNodeXML)
| |
− | ' USAGE: For initializing a node when we don't yet know what Item it will have
| |
− | Me.InitRoot iName, iXML, iParent.Main
| |
− | Set objParent = iParent
| |
− | Set objItem = Nothing ' this will be set later
| |
− | End Sub
| |
− | Friend Property Set Item(iItem As clsSettingAPI)
| |
− | Set objItem = iItem
| |
− | End Property
| |
− | Friend Property Get Item() As clsSettingAPI
| |
− | Set Item = objItem
| |
− | End Property
| |
− | Friend Property Get Main() As clsSettingsXML
| |
− | Set Main = objMain
| |
− | End Property
| |
− | Friend Property Set Main(iDoc As clsSettingsXML)
| |
− | Set objMain = iDoc
| |
− | End Property
| |
− | Public Function Node(iName As String, iCreate As Boolean) As clsSettingsNodeXML
| |
− | ' ACTION: Return the node with the given name
| |
− | Dim objNode As clsSettingsNodeXML
| |
− |
| |
− | ' Set objNode = New clsSettingsNodeXML
| |
− | ' find subnode with name iName; initialize objNode with it (not Nothing)
| |
− | With Me.ListNodes
| |
− | If .Exists(iName) Then
| |
− | Set objNode = .Item(iName)
| |
− | Else
| |
− | If iCreate Then
| |
− | Set objNode = New clsSettingsNodeXML
| |
− | objNode.InitHolder iName, MakeXML(iName), Me
| |
− | Else
| |
− | Set objNode = Nothing
| |
− | End If
| |
− | End If
| |
− | End With
| |
− | Set Node = objNode
| |
− | End Function
| |
− | Public Property Get Name() As String
| |
− | Name = strName
| |
− | End Property
| |
− | Public Property Get ValueStored() As String
| |
− | ' ACTION: Returns the *stored* value (not the active value)
| |
− | If Not (XMLNode Is Nothing) Then
| |
− | ValueStored = XMLNode.Text
| |
− | End If
| |
− | End Property
| |
− | Public Property Let ValueStored(iValue As String)
| |
− | ' ACTION: Sets the *stored* value (not the active value)
| |
− | If XMLNode Is Nothing Then
| |
− | Stop ' does this ever happen?
| |
− | End If
| |
− | XMLNode.Text = iValue
| |
− | End Property
| |
− | Public Property Get ValueActive() As String
| |
− | If Not (objItem Is Nothing) Then
| |
− | ValueActive = objItem.Value
| |
− | End If
| |
− | End Property
| |
− | Public Property Let ValueActive(iValue As String)
| |
− | ' ACTION: Sets the *stored* value (not the active value)
| |
− | If Not (objItem Is Nothing) Then
| |
− | objItem.Value = iValue
| |
− | End If
| |
− | End Property
| |
− | Public Sub Load()
| |
− | ' ACTION: Read values from the XML DOM tree and stash them in clsSettingAPI objects
| |
− | Dim xmlThis As MSXML2.IXMLDOMNode
| |
− | Dim objSub As clsSettingsNodeXML
| |
− |
| |
− | ' 1. Look through the document and pull out values into the value list; save pointer to each node found
| |
− | Set xmlThis = XMLNode.firstChild
| |
− | Do While Not (xmlThis Is Nothing)
| |
− | With xmlThis
| |
− | If Not Me.ListNodes.Exists(.nodeName) Then
| |
− | ' ' this block duplicates code in me.List; should probably put it in a subroutine
| |
− | ' Set objSub = New clsSettingsNodeXML
| |
− | ' objSub.InitHolder .baseName, xmlThis, Me
| |
− | ' lstNodes.Add .baseName, Nothing
| |
− | AddHolder xmlThis
| |
− | End If
| |
− | Set objSub = Me.Node(.nodeName, False)
| |
− | objSub.ValueActive = .Text
| |
− | End With
| |
− | Set xmlThis = xmlThis.nextSibling
| |
− | Loop
| |
− | ' 2. Go through the list and call Load() recursively
| |
− | LoadSub
| |
− | End Sub
| |
− | Private Sub AddHolder(iXML As MSXML2.IXMLDOMNode)
| |
− | Dim objSub As clsSettingsNodeXML
| |
− | Dim strName As String
| |
− |
| |
− | strName = iXML.nodeName
| |
− | Set objSub = New clsSettingsNodeXML
| |
− | objSub.InitHolder strName, iXML, Me
| |
− | lstNodes.Add strName, objSub
| |
− | End Sub
| |
− | Private Sub LoadSub()
| |
− | Dim idxSub As Long
| |
− | Dim objNode As clsSettingsNodeXML
| |
− |
| |
− | For idxSub = 0 To Me.ListNodes.Count - 1
| |
− | Set objNode = Me.ListNodes.Items(idxSub)
| |
− | objNode.Load
| |
− | Next
| |
− | End Sub
| |
− | Public Sub Save()
| |
− | ' ACTION:
| |
− | Dim xmlDoc As MSXML2.DOMDocument
| |
− | Dim xmlParent As MSXML2.IXMLDOMNode
| |
− | Dim xmlThis As MSXML2.IXMLDOMNode
| |
− | Dim strName As String
| |
− |
| |
− | Set xmlDoc = objMain.GetXMLDoc
| |
− | ' ' there doesn't seem to be any way to find out if a child node already exists,
| |
− | ' ' so for now we just kill them all.
| |
− | ' Set xmlThis = xmlNode.firstChild
| |
− | ' Do While Not (xmlThis Is Nothing)
| |
− | ' Set xmlNext = xmlThis.nextSibling
| |
− | ' xmlNode.removeChild xmlThis
| |
− | ' Set xmlThis = xmlNext
| |
− | ' Loop
| |
− | ' If xmlSelf.childNodes.Length <> 0 Then Stop
| |
− | | |
− | If Not (objItem Is Nothing) Then
| |
− | objItem.Save Me ' tell the object to save itself, in case it has any sub-items
| |
− | ' Me.XMLNode.Text = objItem.Value
| |
− | End If
| |
− | ' recursively repeat for all sub-nodes:
| |
− | SaveSub
| |
− | End Sub
| |
− | Private Sub SaveSub()
| |
− | Dim idxSub As Long
| |
− | ' Dim objNode As clsSettingAPI
| |
− | ' Dim objNode As Object
| |
− | Dim objNode As clsSettingsNodeXML
| |
− | Dim strKey As String
| |
− | Dim xmlThis As MSXML2.IXMLDOMNode
| |
− |
| |
− | Set lstXMLs = ListXMLs
| |
− | With Me.ListNodes
| |
− | For idxSub = 0 To .Count - 1
| |
− | Set objNode = .Items(idxSub)
| |
− | strKey = objNode.Name
| |
− | If Not lstXMLs.Exists(strKey) Then
| |
− | ' if an XML node does not already exist, create it and add it to the parent node (i.e. Me)
| |
− | Set xmlThis = objMain.GetXMLDoc.createNode(NODE_ELEMENT, strKey, "")
| |
− | ' Don't save the data here; that's done in objNode.Save()
| |
− | ' xmlThis.Text = objNode.ValueActive
| |
− | Me.XMLNode.appendChild xmlThis
| |
− | Debug.Assert Not (xmlThis.parentNode Is Nothing)
| |
− | End If
| |
− | objNode.Save
| |
− |
| |
− | ' Debug.Print "NAME=" & strName
| |
− | ' Set xmlText = xmlDoc.createTextNode(.Items(intItem))
| |
− | ' xmlThis.appendChild xmlText
| |
− | Next
| |
− | End With
| |
− | End Sub
| |
− | Friend Property Get ListXMLs() As Scripting.Dictionary
| |
− | ' ACTION: get a list of XML subnodes
| |
− | ' maybe this is doing more processing than needed...
| |
− | Dim xmlKid As MSXML2.IXMLDOMNode
| |
− |
| |
− | Set lstXMLs = New Scripting.Dictionary
| |
− | Set xmlKid = Me.XMLNode.firstChild
| |
− | Do Until xmlKid Is Nothing
| |
− | lstXMLs.Add xmlKid.nodeName, xmlKid
| |
− | Set xmlKid = xmlKid.nextSibling
| |
− | Loop
| |
− | Set ListXMLs = lstXMLs
| |
− | End Property
| |
− | 'Friend Function GetXMLNode() As MSXML2.IXMLDOMNode
| |
− | ' Dim xmlDoc As MSXML2.DOMDocument
| |
− | ' Dim xmlRoot As MSXML2.IXMLDOMNode
| |
− | ' Dim xmlNode As MSXML2.IXMLDOMNode
| |
− | '
| |
− | ' Set xmlDoc = objMain.GetXMLDoc
| |
− | '
| |
− | ' If objParent Is Nothing Then
| |
− | ' ' need root node
| |
− | ' If xmlDoc.childNodes.Length = 0 Then
| |
− | ' ' none exists yet; create it:
| |
− | ' Set xmlNode = xmlDoc.createNode(NODE_ELEMENT, Me.Name, "")
| |
− | ' xmlDoc.appendChild xmlNode
| |
− | ' Else
| |
− | ' Set xmlNode = xmlDoc.firstChild
| |
− | ' End If
| |
− | ' Else
| |
− | ' ' create non-root node
| |
− | ' Set xmlNode = xmlDoc.createNode(NODE_ELEMENT, Me.Name, "")
| |
− | ' Set xmlRoot = objParent.GetXMLNode
| |
− | ' xmlRoot.appendChild xmlNode
| |
− | ' End If
| |
− | '
| |
− | ' Set GetXMLNode = xmlNode
| |
− | 'End Function
| |
− | Public Property Get ListNodes() As Scripting.Dictionary
| |
− | Dim xmlDoc As MSXML2.DOMDocument
| |
− | Dim xmlRoot As MSXML2.IXMLDOMElement
| |
− | Dim xmlThis As MSXML2.IXMLDOMNode
| |
− | ' Dim objNew As clsSettingsNodeXML
| |
− | | |
− | If lstNodes Is Nothing Then
| |
− | ' What is often happening at this point is that the XML document has been loaded, but items have not been assigned.
| |
− | ' So we have to set up item-nodes without knowing what objects to assign to them.
| |
− | | |
− | ' 2006-02-12 actually, this list should never be rebuilt from the XML tree. For one thing, that tree has stuff we don't want
| |
− | ' to put in the nodes list. For another, this list is maintained explicitly.
| |
− | ' Correction: unfortunately, we need to be able to list items not explicitly added, for things like dynamic lists
| |
− | Set lstNodes = New Scripting.Dictionary ' create the list object
| |
− | If Not (Me.XMLNode Is Nothing) Then
| |
− | If Me.XMLNode.nodeType <> NODE_TEXT Then
| |
− | ' text nodes get saved along with their parent
| |
− | For Each xmlThis In Me.XMLNode.childNodes
| |
− | With xmlThis
| |
− | ' Set objNew = New clsSettingsNodeXML
| |
− | ' objNew.InitHolder .baseName, xmlThis, Me
| |
− | ' lstNodes.Add .nodeName, .Text
| |
− | AddHolder xmlThis
| |
− | End With
| |
− | Next
| |
− | End If
| |
− | End If
| |
− | End If
| |
− | Set ListNodes = lstNodes
| |
− | End Property
| |
− | 'Public Property Get Exists(iName As String)
| |
− | ' Exists = Me.ListItems.Exists(iName)
| |
− | 'End Property
| |
− | Public Sub LoadTextBox(iCtrl As TextBox, Optional iName As String = "")
| |
− | Dim strName As String
| |
− |
| |
− | If iName = "" Then
| |
− | strName = iCtrl.Name
| |
− | Else
| |
− | strName = iName
| |
− | End If
| |
− | If Me.ListNodes.Exists(strName) Then
| |
− | iCtrl.Text = Me.Node(strName, False).ValueStored
| |
− | End If
| |
− | End Sub
| |
− | Public Sub SaveTextBox(iCtrl As TextBox, Optional iName As String = "")
| |
− | Dim strName As String
| |
− | Dim strValue As String
| |
− |
| |
− | If iName = "" Then
| |
− | strName = iCtrl.Name
| |
− | Else
| |
− | strName = iName
| |
− | End If
| |
− | strValue = iCtrl.Text
| |
− | Me.Node(strName, True).ValueStored = strValue
| |
− | End Sub
| |
− | Public Sub LoadChkBox(iCtrl As CheckBox, Optional iName As String = "")
| |
− | Dim strName As String
| |
− | Dim strValue As String
| |
− |
| |
− | If iName = "" Then
| |
− | strName = iCtrl.Name
| |
− | Else
| |
− | strName = iName
| |
− | End If
| |
− | strValue = Me.Node(strName, False).ValueStored
| |
− | If strValue <> "" Then
| |
− | If strValue = "0" Then
| |
− | iCtrl.Value = vbUnchecked
| |
− | Else
| |
− | iCtrl.Value = vbChecked
| |
− | End If
| |
− | End If
| |
− | End Sub
| |
− | Public Sub SaveChkBox(iCtrl As CheckBox, Optional iName As String = "")
| |
− | Dim strName As String
| |
− | Dim strValue As String
| |
− | Dim objNode As clsSettingsNodeAPI
| |
− |
| |
− | If iName = "" Then
| |
− | strName = iCtrl.Name
| |
− | Else
| |
− | strName = iName
| |
− | End If
| |
− | Set objNode = Me.Node(strName, True)
| |
− | If iCtrl.Value Then
| |
− | strValue = "1"
| |
− | Else
| |
− | strValue = "0"
| |
− | End If
| |
− | objNode.ValueStored = strValue
| |
− | End Sub
| |
− | Public Sub LoadCombo(iCombo As ComboBox, Optional iName As String = "")
| |
− | Dim strName As String
| |
− | Dim strValue As String
| |
− | Dim idxValue As Long
| |
− |
| |
− | If iName = "" Then
| |
− | strName = iCombo.Name
| |
− | Else
| |
− | strName = iName
| |
− | End If
| |
− |
| |
− | strValue = Me.Node(strName, False).ValueStored
| |
− | If IsNumeric(strValue) Then
| |
− | idxValue = CLng(strValue)
| |
− | If idxValue < iCombo.ListCount Then
| |
− | iCombo.ListIndex = CLng(strValue)
| |
− | End If
| |
− | End If
| |
− | End Sub
| |
− | Public Sub SaveCombo(iCombo As ComboBox, Optional iName As String = "")
| |
− | Dim strName As String
| |
− | Dim strValue As String
| |
− |
| |
− | If iName = "" Then
| |
− | strName = iCombo.Name
| |
− | Else
| |
− | strName = iName
| |
− | End If
| |
− |
| |
− | strValue = CStr(iCombo.ListIndex)
| |
− | Me.Node(strName, True).ValueStored = strValue
| |
− | End Sub
| |
− | Private Function MakeXML(iName As String) As MSXML2.IXMLDOMNode
| |
− | Dim objXML As MSXML2.IXMLDOMNode
| |
− |
| |
− | If Me.ListXMLs.Exists(iName) Then
| |
− | Set objXML = lstXMLs.Item(iName)
| |
− | Else
| |
− | Set objXML = objMain.GetXMLDoc.createNode(NODE_ELEMENT, iName, "")
| |
− | Me.XMLNode.appendChild objXML
| |
− | End If
| |
− | Set MakeXML = objXML
| |
− | End Function
| |
− | Private Function clsSettingsNodeAPI_AddItem(iItem As clsSettingAPI) As clsSettingsNodeAPI
| |
− | Dim objNode As clsSettingsNodeXML
| |
− | Dim objXML As MSXML2.IXMLDOMNode
| |
− | | |
− | If Me.XMLNode Is Nothing Then Stop
| |
− | | |
− | If Me.ListNodes.Exists(iItem.Name) Then
| |
− | Set objNode = lstNodes.Item(iItem.Name)
| |
− | Set objNode.Item = iItem
| |
− | Else
| |
− | Set objNode = New clsSettingsNodeXML
| |
− | Set objXML = MakeXML(iItem.Name)
| |
− | objNode.Init iItem, objXML, Me
| |
− | lstNodes.Add iItem.Name, objNode
| |
− | End If
| |
− | End Function
| |
− | Private Property Get clsSettingsNodeAPI_ListNodes() As Scripting.IDictionary
| |
− | Set clsSettingsNodeAPI_ListNodes = Me.ListNodes
| |
− | End Property
| |
− | Private Sub clsSettingsNodeAPI_Load()
| |
− | ' ACTION:
| |
− | ' - Load the current node's value
| |
− | ' - Iterate through children and call Load() for each
| |
− | Dim objNode As clsSettingsNodeXML
| |
− | Dim objKid As clsSettingAPI
| |
− | Dim idxKid As Long
| |
− | | |
− | If Not (objItem Is Nothing) Then
| |
− | objItem.Load Me
| |
− | End If
| |
− | For idxKid = 0 To Me.ListNodes.Count - 1
| |
− | Set objNode = lstNodes.Items(idxKid)
| |
− | Set objKid = objNode.Item
| |
− | objKid.Load objNode
| |
− | Next
| |
− | End Sub
| |
− | Private Property Get clsSettingsNodeAPI_Name() As String
| |
− | clsSettingsNodeAPI_Name = Me.Name
| |
− | End Property
| |
− | Private Function clsSettingsNodeAPI_Node(iName As String, iCreate As Boolean) As clsSettingsNodeAPI
| |
− | Set clsSettingsNodeAPI_Node = Me.Node(iName, iCreate)
| |
− | End Function
| |
− | Private Sub clsSettingsNodeAPI_Save()
| |
− | Me.Save
| |
− | End Sub
| |
− | Private Property Let clsSettingsNodeAPI_ValueStored(RHS As String)
| |
− | Me.ValueStored = RHS
| |
− | End Property
| |
− | Private Property Get clsSettingsNodeAPI_ValueStored() As String
| |
− | clsSettingsNodeAPI_ValueStored = Me.ValueStored
| |
− | End Property
| |
− | </pre>
| |
− | ===clsSettingText===
| |
− | An implementation of [[#clsSettingAPI]]
| |
− | <pre>
| |
− | Option Explicit
| |
− | | |
− | ' CLASS: clsSettingText
| |
− | ' PURPOSE: generic clsSettingAPI class for storing a text value
| |
− | ' without needing an associated control
| |
− | | |
− | Implements clsSettingAPI
| |
− | | |
− | Private strName As String
| |
− | Private strVal As String
| |
− | Public Sub Init(iName As String, iVal As String)
| |
− | strName = iName
| |
− | strVal = iVal
| |
− | End Sub
| |
− | Private Sub clsSettingAPI_Load(iNode As clsSettingsNodeAPI)
| |
− | strVal = iNode.ValueStored
| |
− | End Sub
| |
− | Private Property Let clsSettingAPI_Name(ByVal RHS As String)
| |
− | strName = RHS
| |
− | End Property
| |
− | Private Property Get clsSettingAPI_Name() As String
| |
− | clsSettingAPI_Name = strName
| |
− | End Property
| |
− | Private Sub clsSettingAPI_Save(iNode As clsSettingsNodeAPI)
| |
− | iNode.ValueStored = strVal
| |
− | End Sub
| |
− | Private Property Let clsSettingAPI_Value(ByVal RHS As String)
| |
− | strVal = RHS
| |
− | End Property
| |
− | Private Property Get clsSettingAPI_Value() As String
| |
− | clsSettingAPI_Value = strVal
| |
− | End Property
| |
− | </pre>
| |
− | ===xtForm===
| |
− | Implementation of [[#clsSettingAPI]]
| |
− | <pre>
| |
− | ' CLASS: xtForm
| |
− | ' PURPOSE: extended form functionality
| |
− | ' HISTORY:
| |
− | ' 1999.12.06 - NJS - adapted from Forms.bas
| |
− | ' 2001.01.03 - NJS - modified to use clsConfigReg for storing position
| |
− | ' 2006-01-22 - Wzl - modified to use clsSettingsAPI for storing position
| |
− | | |
− | Implements clsSettingAPI
| |
− | | |
− | Public WithEvents Form As Form
| |
− | Public doSaveSize As Boolean
| |
− | Public doAutoSave As Boolean
| |
− | | |
− | Private strPos As String
| |
− | 'Private objSet As clsSettingsNodeAPI
| |
− | Private strName As String
| |
− | | |
− | Option Explicit
| |
− | Public Sub Init(iForm As Form)
| |
− | Set Form = iForm
| |
− | doSaveSize = (iForm.BorderStyle = vbSizable) Or (iForm.BorderStyle = vbSizableToolWindow)
| |
− | ' Set objSet = iSettings
| |
− | ' Me.LoadPos
| |
− | strName = iForm.Name
| |
− | End Sub
| |
− | Public Property Let Pos(istr As String)
| |
− | strPos = istr
| |
− | If Me.Form.Visible Then Me.PlaceForm
| |
− | End Property
| |
− | Public Property Get Pos() As String
| |
− | ' ACTION: return a string specifying the given form's size and position
| |
− | Dim ostr As String
| |
− | Dim can_size As Boolean
| |
− |
| |
− | With Me.Form
| |
− | If .WindowState <> vbNormal Then
| |
− | .WindowState = vbNormal ' so dimensions are meaningful when we read them
| |
− | End If
| |
− | ostr = .Left & " " & .Top
| |
− | If doSaveSize Then
| |
− | ostr = ostr & " " & .Width & " " & .Height
| |
− | End If
| |
− | End With
| |
− | Pos = ostr
| |
− | End Property
| |
− | 'Public Sub Reveal()
| |
− | '' ACTION: show the given form, and make sure it's not minimized.
| |
− | ' With Me.Form
| |
− | ' .Show
| |
− | ' If .WindowState = vbMinimized Then
| |
− | ' .WindowState = vbNormal
| |
− | ' End If
| |
− | ' LoadPos ' restore saved position
| |
− | ' End With
| |
− | 'End Sub
| |
− | 'Public Sub LoadPos(Optional iKey As String)
| |
− | ' If iKey = "" Then
| |
− | ' iKey = Form.Name
| |
− | ' End If
| |
− | ' Me.Pos = objSet.Value(iKey)
| |
− | ' PlaceForm
| |
− | 'End Sub
| |
− | 'Public Sub SavePos(Optional iKey As String)
| |
− | '' PURPOSE: saves the given form's position to the registry
| |
− | ' With Me.Form
| |
− | ' If iKey = "" Then
| |
− | ' iKey = .Name
| |
− | ' End If
| |
− | ' If .Visible And (.WindowState <> vbMinimized) Then
| |
− | ' objSet.Value(iKey) = Me.Pos
| |
− | ' End If
| |
− | ' End With
| |
− | 'End Sub
| |
− | Public Sub PlaceForm()
| |
− | ' ACTION: set the given form's size and position from the stored string
| |
− | Dim oarr As Variant
| |
− | Dim can_size As Boolean
| |
− |
| |
− | If strPos = "" Then
| |
− | ' use default position: center in screen
| |
− | With Me.Form
| |
− | .Left = (Screen.Width - .Width) / 2
| |
− | .Top = (Screen.Height - .Height) / 2
| |
− | End With
| |
− | Else
| |
− | ' use stored position (and size if applicable)
| |
− | oarr = Split(strPos, " ")
| |
− | With Form
| |
− | .Left = oarr(0)
| |
− | .Top = oarr(1)
| |
− | If .Left > Screen.Width Then
| |
− | .Left = Screen.Width - .Width
| |
− | End If
| |
− | If .Top > Screen.Height Then
| |
− | .Top = Screen.Height - .Height
| |
− | End If
| |
− | If doSaveSize And (UBound(oarr) > 1) Then
| |
− | .Width = oarr(2)
| |
− | .Height = oarr(3)
| |
− | End If
| |
− | End With
| |
− | End If
| |
− | End Sub
| |
− | Private Sub clsSettingAPI_Load(iNode As clsSettingsNodeAPI)
| |
− | strPos = iNode.ValueStored
| |
− | PlaceForm
| |
− | End Sub
| |
− | Private Property Let clsSettingAPI_Name(ByVal RHS As String)
| |
− | strName = RHS
| |
− | End Property
| |
− | Private Property Get clsSettingAPI_Name() As String
| |
− | clsSettingAPI_Name = strName
| |
− | End Property
| |
− | Private Sub clsSettingAPI_Save(iNode As clsSettingsNodeAPI)
| |
− | iNode.ValueStored = Me.Pos
| |
− | End Sub
| |
− | Private Property Let clsSettingAPI_Value(ByVal RHS As String)
| |
− | strPos = RHS
| |
− | PlaceForm
| |
− | End Property
| |
− | Private Property Get clsSettingAPI_Value() As String
| |
− | clsSettingAPI_Value = Me.Pos
| |
− | End Property
| |
− | 'Private Sub Form_Activate()
| |
− | ' EVENT: runs the first time the form is shown
| |
− | ' Me.PlaceForm
| |
− | 'End Sub
| |
− | Private Sub Form_Deactivate()
| |
− | strPos = Me.Pos ' get new position
| |
− | End Sub
| |
− | Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
| |
− | ' This is redundant; xtForm also handles this
| |
− | ' If doAutoSave Then
| |
− | ' Me.SavePos
| |
− | ' End If
| |
− | End Sub
| |
− | </pre>
| |
− | ===xtListBox===
| |
− | Implementation of [[#clsSettingAPI]]
| |
− | <pre>
| |
− | ' CLASS: xtListBox
| |
− | ' PURPOSE: extended functionality for ListBox control
| |
− | ' REQUIRES: xtListItems.cls, xtListItem.cls, property let/get AppSetting(Name)
| |
− | Option Explicit
| |
− | Implements clsSettingAPI
| |
− | | |
− | Public Event ItemChosen(oKey As Long)
| |
− | Public Event ItemChecked(oKey As Long)
| |
− | | |
− | 'Public Config As clsSettingsNodeAPI
| |
− | | |
− | 'local variable(s) to hold property value(s)
| |
− | Private strName As String
| |
− | Private WithEvents ctrlList As ListBox 'local copy
| |
− | Private WithEvents ctrlForm As Form
| |
− | Private lstItems As Scripting.Dictionary
| |
− | Private doDataSave As Boolean
| |
− | Private intNewItem As Long
| |
− | Public Sub Init(iList As ListBox, Optional iDataSave As Boolean = True)
| |
− | Set ctrlList = iList
| |
− | Set ctrlForm = iList.Parent
| |
− | ' Set Config = iConfig
| |
− | Set lstItems = New Scripting.Dictionary
| |
− | doDataSave = iDataSave
| |
− | With ctrlList
| |
− | strName = .Parent.Name & "." & .Name
| |
− | End With
| |
− | End Sub
| |
− | Public Property Get List() As Scripting.Dictionary
| |
− | Set List = lstItems
| |
− | End Property
| |
− | Public Sub Add(iText As String, Optional iObj As Object)
| |
− | ' NOTES:
| |
− | ' 1. Add object to list
| |
− | ' 2. Add text to listbox, with ItemData pointer to list
| |
− | With ctrlList
| |
− | intNewItem = intNewItem + 1
| |
− | .AddItem iText
| |
− | .ItemData(.NewIndex) = intNewItem
| |
− | lstItems.Add intNewItem, iObj
| |
− | End With
| |
− | End Sub
| |
− | Public Sub Clear()
| |
− | ctrlList.Clear
| |
− | lstItems.RemoveAll
| |
− | End Sub
| |
− | Public Property Get SelectedItem() As Object
| |
− | ' to be written
| |
− | End Property
| |
− | Public Function SelectCount() As Integer
| |
− | SelectCount = ctrlList.SelCount
| |
− | End Function
| |
− | Public Sub ClearSelection()
| |
− | ' PURPOSE: removes any selections from the control
| |
− | Dim idxList As Long, qtyList As Long
| |
− |
| |
− | qtyList = ctrlList.ListCount
| |
− | For idxList = 1 To qtyList
| |
− | ctrlList.Selected(idxList) = False
| |
− | Next
| |
− | End Sub
| |
− | Public Sub DelSelected()
| |
− | ' ACTION: Delete selected items from the list
| |
− | Dim idxList As Long, qtyList As Long
| |
− | Dim idxDel As Long
| |
− |
| |
− | qtyList = Me.List.Count
| |
− | For idxList = qtyList - 1 To 0 Step -1
| |
− | ' find the item's key in the stored list
| |
− | idxDel = lstItems.Keys(idxList)
| |
− | If ctrlList.Selected(idxList) Then
| |
− | ' delete item from the stored list
| |
− | lstItems.Remove idxDel
| |
− | ' delete item from the control
| |
− | ctrlList.RemoveItem idxList
| |
− | End If
| |
− | Next
| |
− | End Sub
| |
− | Public Sub KeyDown(KeyCode As Integer, Shift As Integer)
| |
− | '' PURPOSE: define additional standard behavior for non-ASCII key presses
| |
− | '' ACTION: Shift-Insert copies list contents to clipboard
| |
− | ' If ((KeyCode = vbKeyInsert) And (Shift & &H1)) Then
| |
− | ' ClipCopy
| |
− | ' End If
| |
− | End Sub
| |
− | Private Sub clsSettingAPI_Load(iNode As clsSettingsNodeAPI)
| |
− | ' ACTION: Load current selection and, if applicable, list data
| |
− | Dim qtyItem As Long
| |
− | Dim idxItem As Long
| |
− | Dim objNode As clsSettingsNodeAPI
| |
− | Dim strVal As String
| |
− |
| |
− | ' Me.Value = iNode.ValueStored
| |
− | ' check for saved data list
| |
− | qtyItem = iNode.ListNodes.Count
| |
− | ' load data, if there is any
| |
− | For idxItem = 0 To qtyItem - 1
| |
− | Set objNode = iNode.ListNodes.Items(idxItem)
| |
− | strVal = objNode.ValueStored
| |
− | If objNode.Name = "selected" Then
| |
− | Me.Value = objNode.ValueStored
| |
− | Else
| |
− | If strVal <> "" Then
| |
− | ' klugefix
| |
− | If Not Me.List.Exists(strVal) Then
| |
− | Me.Add strVal
| |
− | End If
| |
− | End If
| |
− | End If
| |
− | Next
| |
− | End Sub
| |
− | Private Property Let clsSettingAPI_Name(ByVal RHS As String)
| |
− | strName = RHS
| |
− | End Property
| |
− | Private Property Get clsSettingAPI_Name() As String
| |
− | clsSettingAPI_Name = strName
| |
− | End Property
| |
− | Private Sub clsSettingAPI_Save(iNode As clsSettingsNodeAPI)
| |
− | Dim idxItem As Long
| |
− | Dim objNode As clsSettingsNodeAPI
| |
− |
| |
− | Set objNode = iNode.Node("selected", True)
| |
− | objNode.ValueStored = Me.Value
| |
− | If doDataSave Then
| |
− | For idxItem = 0 To Me.List.Count - 1
| |
− | Set objNode = iNode.Node("line-" & CStr(idxItem), True)
| |
− | objNode.ValueStored = ctrlList.List(idxItem)
| |
− | Next
| |
− | End If
| |
− | End Sub
| |
− | Public Property Let Value(iValue As String)
| |
− | Dim xtsVal As xtString
| |
− | Dim idxSel As Long
| |
− | Dim strSel As String
| |
− |
| |
− | Set xtsVal = New xtString
| |
− |
| |
− | xtsVal.Value = iValue ' data is list of selected items, by index
| |
− | ' parse data
| |
− | strSel = xtsVal.FindFirst
| |
− | Do While xtsVal.IsFound
| |
− | If IsNumeric(strSel) Then
| |
− | idxSel = CLng(strSel)
| |
− | ctrlList.Selected(idxSel) = True
| |
− | End If
| |
− | strSel = xtsVal.FindNext
| |
− | Loop
| |
− | End Property
| |
− | Public Property Get Value() As String
| |
− | Dim strSel As String
| |
− | Dim idxItem As Long
| |
− |
| |
− | ' debugging:
| |
− | ' If Me.List.Count <> ctrlList.ListCount Then Stop
| |
− | | |
− | For idxItem = 0 To ctrlList.ListCount - 1
| |
− | If ctrlList.Selected(idxItem) Then
| |
− | strSel = strSel & " " & idxItem
| |
− | End If
| |
− | Next
| |
− | Value = strSel
| |
− | End Property
| |
− | Private Property Let clsSettingAPI_Value(ByVal RHS As String)
| |
− | Me.Value = RHS
| |
− | End Property
| |
− | Private Property Get clsSettingAPI_Value() As String
| |
− | clsSettingAPI_Value = Me.Value
| |
− | End Property
| |
− | Private Sub ctrlList_Click()
| |
− | Dim intLine As Integer
| |
− | Dim intKey As Long
| |
− | | |
− | intLine = ctrlList.ListIndex
| |
− | intKey = ctrlList.ItemData(intLine)
| |
− | If intKey Then
| |
− | RaiseEvent ItemChosen(intKey)
| |
− | End If
| |
− | End Sub
| |
− | Private Sub ctrlList_ItemCheck(Item As Integer)
| |
− | Dim intKey As Long
| |
− | | |
− | If Me.List.Exists(Item) Then
| |
− | intKey = ctrlList.ItemData(Item)
| |
− | RaiseEvent ItemChecked(intKey)
| |
− | End If
| |
− | End Sub
| |
− | </pre>
| |