PDA

View Full Version : Solved: How do you create a VBA variable for an instance of a document?



rruckus
10-27-2010, 12:00 PM
This is probably easy but I can't find help on it anywhere, maybe I'm wording it wrong. I want to have an instance of a variable for each document instance that is running my code (any document attached to my .dotm template). I need this so I can remember specific properties of a document that don't exist on the Word document. I can't use document variables because sometimes my variable may be an object, form or something other than a string.

For example, I want to keep my own boolean flag (that my code sets) that is always False whenever the document is opened (or created as new from template) but then after a user does a specific action in a document, it is set to True, but only for that document. So when I switch the other document, it is still False.

If I create a global variable in a module, it's global to ALL documents, so changing the variable in one document updates it for any document which is not what I need.

Basically I need access to ActiveDocument.mycustomvariable. How do I do this???

fumei
10-27-2010, 12:54 PM
Seems you are asking contradictary things.

"If I create a global variable in a module, it's global to ALL documents, so changing the variable in one document updates it for any document which is not what I need."

Start by stating what you DO need.

"I need this so I can remember specific properties of a document that don't exist on the Word document."

The specific properties of a document that does not have those properties existing? This simply does not make sense.



"I want to have an instance of a variable for each document instance that is running my code (any document attached to my .dotm template). "

1. documents are not instances;
2. documents themselves are not running code;
3. documents are not attached to your dotm. Your dotm is attached to the documents.


"Basically I need access to ActiveDocument.mycustomvariable"

That sounds like document variables. They are custom.


"I can't use document variables because sometimes my variable may be an object, form or something other than a string."

You are going to have to to explain THAT in better detail. So you want some super duper variable - a Variant perhaps - that can be an object, a form (which is an object BTW...) or a string. Hmmmm. Please expand on this.

fumei
10-27-2010, 12:57 PM
BTW: I am not trying to give you a hard time. It is quite possible that what you are trying to do is possible, but it is not clear what it is you ARE trying to do. Also it is possible that what you intend/wish to do can be achieved by some other means. Hard to say.

rruckus
10-27-2010, 01:17 PM
No worries, I realize my explanation is confusing. That's probably why I cant find a solution online. Hopefully this is clearer:

"I need to remember specific properties of a document that don't exist on the Word document."

Specifically, I want my own IsDirty property that I can set to True when the user does a specific operation. It's not enough to used ActiveDocument.Saved or similar because I only want it to be dirty when the user interacts with a specific form I created.

I also need to store a collection of items and an object with my document.

Tinbendr
10-27-2010, 02:21 PM
To answer your question, object.Variables("Name").Value is what you need.

So, if you're adding a new document based on a template


Dim IsDirty as Boolean

Set MyDoc = Documents.Add MyTemplate

IsDirty = MyDoc.Variables("Custom1").Value

rruckus
10-27-2010, 02:40 PM
That's not right either because of my 2nd requirement of storing objects and forms inside of the variables. As far as I see, document variables can only be simple data types like numbers and strings.

As I said before, "I can't use document variables because sometimes my variable may be an object, form or something other than a string."

So I need ActiveDocument.myinstanceofmyform.myformproperty

fumei
10-29-2010, 10:06 AM
This is not possible, at least as far as you have stated your requirement.

You can NOT store objects in a variable. A variable has a data type. You can declare an object (and then Set it).

You have still not really explained what it is you really need.

"I only want it to be dirty when the user interacts with a specific form I created."

You can not make a single "variable" to hold various states (objects vs userform vs whatever). But you can create a - ahem, DOCVARIABLE (as has been suggested twice) - for each of the things you want to track.

So say UserformBLAH is displayed.
Sub UserFormBLAH_Initialize()
ActiveDocument.Variables("BlahOpened").Value = 1
End Sub


Now, if you want to later see if userformBLAH has been opened, you test the Variable. If = 1, then yes the userform has been opened.

Your: ActiveDocument.myinstanceofmyform.myformproperty

is NOT possible (again, at least as you have defined it).

Once a userform (for example) is displayed (opened) and then closed, it no longer exists. There is no "instance" anymore.

You can ONLY get myformproperty when myform is alive (so to speak).

fumei
10-29-2010, 10:06 AM
But again, it would help immensely if you fully explained what you are trying to do.

rruckus
10-29-2010, 10:56 AM
Thanks guys, I tried the document variables solution, but it doesn't work because you can only store STRINGS in document variables, not objects as I previously stated. Under your proposed solution, I tried:


Dim x As New Scripting.Dictionary
ActiveDocument.Variables.Add "sMyDictionary", x

Dim z(0 To 10) As String
ActiveDocument.Variables.Add "sMyArray", z




But it fails with a "Type Mismatch" error (as I expected).

Here's a complete description of the specific problem: A user double clicks my template (that holds my code), creating a new document instance of my template. The code auto-downloads a support file from my website, which lists the named Content Controls (CCs) allowed to be inserted in my document. Right now, the code stores the named CC's in a global scripting dictionary (g_AllowedCCs). The user authors for an hour and then decides to create another new file from my template. The download happens again, updating the global dictionary, but meanwhile the list on my server has changed so the list of allowed CCs is different (which is ok for this document). The user switches back to the original document and goes to insert a named CC and the list is WRONG. Because I am using a global dictionary object, it has been updated by the download for ALL documents. But my list of allowed CC's in my original document should still only contain the original list - not the updated one.

Is that clearer? Thanks for your patience!

fumei
10-29-2010, 02:41 PM
"Right now, the code stores the named CC's in a global scripting dictionary (g_AllowedCCs)."

Oh no it does not. It stores the strings of the names. The objects themslves are not in there.

But let me see if I understand what you are saying.

User creates a document from the template. The template downloads data in a scripting dictionary. These are the CC available. The user messes around for a while, and then uses the template to creates ANOTHER document.

In the meantime YOU (or something) has changed that dictionary (or rather the data used to populate that dictionary.

The SAME template now downloads different values in the dictionary.

You want to somehow have the first document remember what WAS in the dictionary.

Nope. Sorry. If you changed it, and use the SAME dictionary (which has different values now) then yes that is the dictionary you have available.

The dictionary has been changed. Period.

One thing you could do is have the template import a .bas file which creates the dictionary in the document (rather than having the template contain the dictionary).

But as it stands if the dictionary is a part of the template, if you change it, then all documents with that template attached use the same dictionary, whatever values it holds. Each document reads back to the template.

rruckus
11-01-2010, 10:48 AM
That doesn't work for me; I can't have code in my template instances. I figured it out on my own. Here's the solution:

1. Create a Class Module that registers the events of the Word application and Document:


Private WithEvents m_oWordApp As Word.Application
Private WithEvents m_oWordDoc As Word.Document



2. Create a Class Module to hold your objects you want to associate with your documents (with public Get/Set properties). THESE CAN BE OBJECTS.

3. On WordDoc_New event, download the file, create the scripting dictionary from the download, and store as new instance of the class from step #2 in a global collection. Use the active window handle as the collection key.


Public Declare Function FindWindowByClass Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As Long) As Long

Dim lngHWnd As Long
lngHWnd = FindWindowByClass("opusapp", 0&)



4. On WordApp_DocumentChange find the right object in the global collection by window handle and set as your global object you want to work with.

5. On doc close, remove the entry from the global collection.

Now, whenever you want to find a variable for a specific document, it will always be in your current global object.

fumei
11-01-2010, 11:25 AM
Elegant. Thanks for posting your solution.