VBA edit cell in master template file - vba

I have a button in a template (XLT) that saves the new workbook to a directory. The filename needs to auto-increment from the one used previously. I can't rely on a directory search as the documents may be moved/deleted over time.
Is it possible to auto-increment a cell in the template file, from code running in the new workbook?

As long as it is possible to save the template, you could store the value in a Name (these are most commonly used to store ranges, but can be used to store constants).
When you create the Name, just type a value instead of a range into the RefersTo area. Then you can access it like this:
Sub WhatsTheValue()
Dim intValue As Integer
intValue = Evaluate(ThisWorkbook.Names("NextValue").RefersTo)
ThisWorkbook.Names("NextValue").Value = intValue + 1
MsgBox intValue & vbNewLine & Evaluate(ThisWorkbook.Names("NextValue").RefersTo)
End Sub
The code will get the current value of the NextValue Name, assign it to the intValue variable and will immediately increment the Name.
You would, of course, have to save the template each time. This may cause problems if multiple people are accessing the template.

Related

Saving Custom Document Properties in a Loop

I'm trying to save the values of data that have been input into my form. There are a total of about 50 different fields to save across 5 different agents, so I loaded the data into arrays.
I've tried saving the fields in a loop, but it doesn't seem to work in a loop, only if each field has a separate line, which is a lot of code and messy. The Ag1Name, Ag2Name and Ag3Name are the names of my textboxes that the user enters to populate the form.
Sub LoadAndSaveData()
NumberofAgents = 3
Dim AgentName(3) as String
AgentName(1) = Ag1Name.Value
AgentName(2) = Ag2Name.Value
AgentName(3) = Ag3Name.Value
For Count = 1 To NumberOfAgents
With ActiveDocument.CustomDocumentProperties
.Add Name:="AgentName" & Count, LinkToContent:=False, Value:=AgentName(Count), Type:=msoPropertyTypeString
End With
Next Count
End Sub
The data doesn't get saved to the Custom Document Properties when the code is set up in a loop like the above. Since there are so many values to save and all the data is already in arrays, I would much prefer to use a loop rather than write out a separate line of code for all ~50 of the values. It does seem to work when each field is saved in a separate line of code.
I think this would probably get what you want. You don't really need to count the document properties first, only increment with the ones you want to update. Hopefully the only document properties you want contain the name AgentName in it.
ReDim AgentName(0) As String
Dim P As Long
For Each c In ThisDocument.CustomDocumentProperties
If InStr(1, c.Name, "AgentName", vbTextCompare) > 0 Then
ReDim Preserve AgentName(P)
AgentName(P) = c.Value
P = P + 1
End If
Next c
As a guest I cannot post a comment here, but the code you gave works OK here.
However, there is a problem with creating legacy custom document properties programmatically, because doing that does not mark the document as "changed". When you close the document, Word does not necessarily save it and you lose the Properties and their values.
However, if you actually open up the Custom Document Property dialog, Word does then mark the document as "changed" and the Properties are saved.
So it is possible that the difference between your two scenarios is not the code, but that in one scenario you have actually opened the dialog box to check the values before closing the document and in the other you have not.
If that is the case, here, I was able to change this behaviour by adding the line
ActiveDocument.Saved = False
after setting the property values.
If you do not actually need the values to be Document Properties, it might be better either to use Document Variables, which are slightly easier to use since you can add them and modify them with exactly the same code, or perhaps by storing them in A Custom XML Part, which is harder work but can be useful if you need to extract the values somewhere where Word is not available.
You can make this even easier by looping the controls on the UserForm, testing whether the control name contains "Ag" and, if it does, create the Custom Document Property with the control's value - all in one step.
For example, the following code sample loops the controls in the UserForm. It tests whether the controls Name starts with "Ag". If it does, the CustomDocumentProperty is added with that control's value.
Sub LoadAndSaveData()
Dim ctl As MSForms.control
Dim controlName As String
For Each ctl In Me.Controls
controlName = ctl.Name
If Left(controlName, 2) = "Ag" Then
With ActiveDocument.CustomDocumentProperties
.Add Name:=controlName, LinkToContent:=False, value:=ctl.value, Type:=msoPropertyTypeString
End With
End If
Next
End Sub
I feel a little stupid... I just realized that the reason that the code wasn't working was that the variable NumberofAgents was not being calculated correctly elsewhere in my code. I've got it working now. Thanks for your thoughts!

How do you save data input into a macro so that the input form can be populated with the data next time it runs?

I need to save data that was input into a Microsoft Word form in VBA after the macro terminates. I have a form that has about 40 fields with names, addresses, phone numbers etc and I don't want the user to have to input everything again in case they realize that they made a mistake and need to change 1 item. Currently, the macro deletes all the data that was input into the form when the form closes.
I've looked around on forums and Google but haven't had any luck.
This code brings up my input form from a word document with a command button:
Private Sub CommandButton1_Click()
InputForm.Show vbModeless
End Sub
This code closes the form:
Private Sub CloseForm_Click()
Dim Closing As Integer
Closing = MsgBox("This will exit the form and erase all the data that has been input. You may want to review the documents to ensure they were generated correctly before closing the form. Click Yes to proceed to close the form or No to go back to the form.", vbYesNo + vbQuestion, "Exit Confirmation")
If Closing = vbYes Then
Unload InputForm 'This closes the Input Form and returns the user to Word
End If
End Sub
The macro does not save the information input into the form. Whenever it closes, all the information is erased; I want it saved.
Options for saving data in the document
Document.Variables This has been available in Word since the very early days and is available in all versions since Word 2.0 (early 1990s). It's simply a place to store any number of strings, associated with an identifier (name). In essence, a key-value pair. Important to note: It must be a pair, there cannot be a key without a value and a value cannot be a zero-length string. Assigning a value to a name creates the Variable; deleting the value removes the Variable. These are not visible to the user.
Example:
Document.Variables("Name").Value = "John Doe"
Document.CustomDocumentProperties Similar to Variables but data-types other than strings are available. There is a limit to the length of the data that can be stored. They are visible to the user through the "Properties" interface. They must be explicitly created / destroyed. This was introduced in, I believe, Word 97.
Example:
Document.CustomDocumentProperties.Add(Name As String, LinkToContent As Boolean, _
[Type], [Value], [LinkSource])
CustomXMLParts These are XML "pages" saved in the docx zip package. This is a good way to save data, and it's easily accessible from the closed file. The capability was introduced in Word 2010, so won't be available in older versions nor in doc file formats, only docx.
The coding is more complex than for Variables or Properties.
The usual way to work with this in the described scenario is to create the CustomXMLPart in the document (or template - documents created from the template should carry over the CXP). Then all the code needs to do is read/write from the xml nodes already available. If the VBA coder has no background in XML parsing there will be a steep learning curve.
There are many ways to go about reading and writing, here's a snippet that demonstrates the basics of reading, to give an idea of what's involved:
Sub ReadCXP()
Dim cxp As Office.CustomXMLPart, CXPs As Office.CustomXMLParts
Dim doc As Word.Document
Dim sUri As String
Set doc = ActiveDocument
'object, not collection, because ID is unique and can return only one
'ID is unique to each CustomXMLPart - this is won't work as it stands!
Set cxp = doc.CustomXMLParts.SelectByID("{32B20BB8-F4FB-47D6-9DF4-FFAF5A1D8C18}")
Dim xmlNds As Office.CustomXMLNodes
Dim xmlNd As Office.CustomXMLNode
Set xmlNds = cxp.SelectSingleNode("trees").ChildNodes
For Each xmlNd In xmlNds
Debug.Print xmlNd.XML
Next
End Sub

Open File based on Dictionary Value matched to Public Variable

I have an excel file with a list of codes in column 1 relating to different workbooks.
At first I created a code that would loop through the codes and I would manually open the specific workbook using "Application.GetOpenFilename".
Now, I decided to make it more efficient and because the file path doesn't change I figured I'd create a global public variable with the file path location.
I create a dictionary that adds all the codes. (That is the loop between 2 and 50) The value in each row of Cells(i,1) contains an abbreviated code which is then tied to a public variable with the full file path/location. In this case, all the codes in rows 2 through 50 are ABC123 (key = ABC123). Other times I have multiple codes, which is why I add to a dictionary to keep the unique values and can then open these individual files.
I was hoping to loop through the dictionary to open the specific files related to the code, instead it tried to open the string value rather than the file path. ABC123.xls rather than C:\Users\xxxx\xxxx\ImportantFile2018.xls
I'm trying to figure how to get the dictionary key to equal the public variable I assigned with the same name.
Do not get hung up on variable names, these are just examples. The major issue is how to retrieve the variables value (file path) when the variable name (ABC123) matches a cell's value. Every cell value in column 1of the data has a matching variable with a filepath.
Option Explicit
Public ABC123 As String
Sub DefinedVariables()
ABC123 = C:\Users\xxxx\xxxx\ImportantFile2018.xls
End Sub
Sub Reporting_Update()
Dim dictClient As New Scripting.Dictionary
Dim key As Variant
Call DefinedVariables
For i = 2 To 50
If dictClient.Exists(Cells(i, 1).Value) Then
Else:
dictClient.Add Cells(i, 1).Value
End If
Next
For Each key In dictClient
Workbooks.Open FileName:=key

VBA Excel - copy range from a second file to a vba-range object

i wanted to write a excel-vba module accessing date from second excel-file.
i first wanted to open the second file to a vba-range object, copy the worksheet-range to a vba-range-object and close the second file afterwards.
data-processing now happens only on the vba-range object.
i tried the following:
Set oMeasuresWorkbook = Workbooks.Open(sMeasuresFileName)
Set oSrcRange = oMeasuresWorkbook.Names("MEASURES").RefersToRange
MsgBox oSrcRange(1, 1)
oMeasuresWorkbook.Close
'problem: after closing the second file (oMeasuresWorkbook) the
oSrcRange Object is gone
MsgBox oSrcRange(1, 1) 'error here as the oSrcRange Object is gone
everything works fine until i close the second file. seemingly, oSrcRange is a reference to the original data. btw. same behavior if i access the range by for eg.
Set oSrcRange = oMeasuresWorkbook.Sheets(1).Range("A:G")
So how would I "deep copy" the range. i tried range.copy but semmingly i do not know how i would initialize a range object (and i do not want to copy the data to a worksheet).
i hope i made myself clear and sombody can help
thanx !
Try something like this instead:
Dim SrcRangeArray as Variant
Set oMeasuresWorkbook = Workbooks.Open(sMeasuresFileName)
Set oSrcRange = oMeasuresWorkbook.Names("MEASURES").RefersToRange
SrcRangeArray = oSrcRange.Value
MsgBox SrcRangeArray (1, 1)
oMeasuresWorkbook.Close
'problem: after closing the second file (oMeasuresWorkbook) the oSrcRange Object is gone
MsgBox SrcRangeArray (1, 1)
As far as I know, there isn't a way to store a copy of an Object solely in memory where it is then no longer affected by changes to the original object. Objects are storing actual objects, and not the values of those objects.
In my code above, I instead take the value from the range and put it into an array. These values will persist, even if the original object is closed. Note though that you can now only work on those values. You can't 'Close' or 'Open' the array for example, since it is just values.
I left the original object there for demonstration, but if you dont need the object, and you do only need its values, I would just skip the middleman and go with the array instead.

Populate a userform Combobox with a list of subdirectory names in a defined directory

I apologize if this question was answered previously on this board. My searches didn't turn up what I'm looking for. I am a VBA novice and would like to know if there is a way to populate a userform combobox with the names of all subdirectories contained within a predefined directory (I need the list to be updated every time the userform is launched). I've seen some code that does this on other websites but they were for earlier versions of Excel and I could not get them to work. I am using Excel 2007. I appreciate any help you may be able to provide.
Option Explicit
Private Sub UserForm_Initialize()
Dim name
For Each name In ListDirectory(Path:="C:\", AttrInclude:=vbDirectory, AttrExclude:=vbSystem Or vbHidden)
Me.ComboBox1.AddItem name
Next name
End Sub
Function ListDirectory(Path As String, AttrInclude As VbFileAttribute, Optional AttrExclude As VbFileAttribute = False) As Collection
Dim Filename As String
Dim Attribs As VbFileAttribute
Set ListDirectory = New Collection
' first call to Dir() initializes the list
Filename = Dir(Path, AttrInclude)
While Filename <> ""
Attribs = GetAttr(Path & Filename)
' to be added, a file must have the right set of attributes
If Attribs And AttrInclude And Not (Attribs And AttrExclude) Then
ListDirectory.Add Filename, Path & Filename
End If
' fetch next filename
Filename = Dir
Wend
End Function
A few notes, since you said you had little experience with VBA.
Always have Option Explicit in effect. No excuses.
Dir() is used in VB to list files.
Collections are a lot more convenient than arrays in VBA.
There are named parameters available in function calls (name:=value). You don't have to use them, but they help to make sense of long argument lists. Argument order is irrelevant if you use named parameters. You cannot mix named and unnamed parameters, though.
You can have optional arguments with default values.
Note that assigning to the function name (ListDirectory in this case) sets the result of a function. You can therefore use the function name directly as a variable inside that function.
Set AttrInclude to -1 if you want to return all types of files. Conveniently, -1 is the numerical value of True., i.e. ListDirectory("C:\", True).
Set AttrExclude to 0 if you want to exclude no files. Conveniently, 0 is the numerical value of False., i.e. ListDirectory("C:\", True, False), which also is the default.
All logical operators in VB 6.0 are bit-wise, hence you can check whether a file is a directory by using If Attribs And VbDirectory Then ...
You can combine multiple bit values with Or, e.g. vbSystem Or vbHidden.
Consequently, you can filter directories with a simple bit-wise logic check.
Use the Object Browser (hit F2) to inspect available Functions, Types and Constants, for example the constants in the VbFileAttribute enum.