Save file with name from form content - vba

I'm trying to save a Microsoft Word 2013 document with a specific filename.
I created a Word form that a client fills out.
I am using the following: DEVELOPER -> Controls -> Plain Text Content Control DEVELOPER -> Controls -> Date Picker Content Control DEVELOPER -> Controls -> Drop-Down List Content Control
I would like a macro to save the document with the name of one of the fields in the form.
Example:
below are fillable content fields.
client reference: A1B2-345
date: August 17, 2015
type: metadata
I would like to save the file as:
A1B2-345_17082015_metadata.DOCX

Start by giving each of your controls a Title. You can do this by clicking on your control in your document and then click Properties on the Developer ribbon. The first field on the Properties window is Title. Give each one of the controls you need to access a unique title. In the example below, I'm using MyText, MyDate, and MyDrop for the text, date, and drop-down controls, respectively.
Then you can access each control in VBA by using the SelectContentControlsByTitle() function. As long as you're using a unique title, this function will return a collection containing only a single control so we can just retrieve the first item from the collection (index (1)). Here's how this would look:
Dim strText As String, strDate As String, strDrop As String
strText = ThisDocument.SelectContentControlsByTitle("MyText")(1).Range.Text
strDate = ThisDocument.SelectContentControlsByTitle("MyDate")(1).Range.Text
strDrop = ThisDocument.SelectContentControlsByTitle("MyDrop")(1).Range.Text
The Range.Text ending is what grabs the current text/value from the control.
Now that you have your three pieces of information, you can concatenate them into one string using the concatenation operator (&):
Dim strFilename As String
strFilename = strText & "_" & Format(strDate, "ddmmyyyy") & "_" & strDrop & ".docx"
And, finally, save it:
ThisDocument.SaveAs strFilename

It should be noted that the code provided above will not work properly if you are generating the form based off a template (.dotm). The following lines that include the ThisDocument.SelectContentControlsByTitle need to be changed to
ActiveDocument.SelectContentControlsByTitle otherwise the code will pull the place holder text from the content control.

Related

How to update an add-in field code without losing field data (MS Word)?

I have an add-in field in MS Word which also has some hidden data. I want to update the field's code text. However, when I do so and read my hidden data again, they are gone. See this minimal example:
Sub testUpdatingFieldCode()
Dim newField As Field
Set newField = ActiveDocument.Fields.Add(Range:=Selection.Range, Type:=wdFieldAddin)
newField.Code.Text = " ADDIN mycode \* MERGEFORMAT "
newField.Data = "mydata"
Debug.Print "Code: " & newField.Code.Text
Debug.Print "Data: " & newField.Data ' works - prints "Data: mydata"
newField.Code.Text = " ADDIN mycodechanged \* MERGEFORMAT "
Debug.Print "Code: " & newField.Code.Text
Debug.Print "Data: " & newField.Data ' doesn't work - prints "Data: "
End Sub
Is this normal behaviour or a bug? Is there a suggested way to update field codes without losing the data?
I could save the Data to a variable like this but I'm worried about performance (I ultimately need to do this for tens or hundreds fields at a time):
Dim previousData As String: previousData = newField.Data
newField.Code.Text = " ADDIN my_code_changed \* MERGEFORMAT "
newField.Data = previousData
Trying your code, it does insert the field and does change the value.
The problem seems to be with .Data because the field is there in the document.
I join John Korchok's suggestion that you use Document variables instead. They can hold text information and it is not normally accessible to the user. It is not in the text of the document and so not subject to accidental deletion by the user. You can have multiple variables in a document. They can be displayed in the document if you want through a DocVariable field which is a reporting, not an editing, field.
Here is a link to the support document on document variables. Two code examples from that page:
ActiveDocument.Variables.Add Name:="Value1", Value:="1"
MsgBox ActiveDocument.Variables("Value1") + 3
For Each myVar In ActiveDocument.Variables
MsgBox "Name =" & myVar.Name & vbCr & "Value = " & myVar.Value
Next myVar
As you can see, this is a much simpler construction than using the AddIn Field. There are several utilities available that give access to document variables from within the User Interface but nothing in Word out of the box that allows the user to change variables without vba.
[EDIT to address concerns in first comment.]
You can use the Feedback mechanism in Word to complain about the problem with the Addin field. I do not know that it will be fixed because there are such better tools available. More likely is that they will delete or deprecate the field. I know of no way to work around this and get the result you want. Even deleting the field and recreating it does not allow you to get the data.
You can use Document Properties as well as Document Variables. Those are available to the user through the Advanced Properties. In addition, certain properties are available through the Quick Parts menu where a change in the property content control in the document changes the property itself. These are mapped to XML nodes. Like Document Variables, all Document Properties can be displayed through a (reporting) field: DocProperty. My page on Repeating Data shows some uses that can be made using these built-in mapped Document Property Content Controls.

Making QRCode ActiveX Control for MS Access: Control Source Property

I wanted to implement a QR Code to Access2010 and I found https://github.com/yas78/QRCodeLibVBA. Referencing XLAM from Access did not work and I didn't want to insert all modules directly to Access as it makes the project messy.
So I decided to create an OCX file using ancient VB6 as it seemed to be the easiest way to encapsulate all the bits together into one simple object.
Finally, I have made an OCX that has several key properties: DataString which is the string to be displayed, ByteModeCharsetName, ErrorCorrectionLevel, ForeRGB and BackRGB, there are also methods Refresh, Cls and events OnClick and OnDblClick
It works fine in VB6 apps + Excel sheets + Excel forms but it behaves weird in Access forms, reports, etc.
Everything looks as one would expect in Excel:
This is how it looks in Access:
The custom properties are visible on the tab "Other" but they are not offered in VBA editor at all! However it does compile when entered manually.
Resizing control behaves weird
Control's Events like OnClick are not displayed at tab Event of Property Sheet
Here are my questions:
Are the Controls for Access "different" than for other office apps?
Why the hell are the properties hidden in editor?
How to "move" some properties to other tabs (categories), for example ForeRGB to tab Format (as usual for TextBoxes etc.)?
How to create ControlSource propety (on the DATA tab) which could be directly bound to a recordset without having to use a VBA? This way, I hope, I could use the control on the continuous forms as well. In fact, this is most important question.
Some tips for resizing? (not important)
I think I'm pretty close to my goal but I'm stuck at this point. I know the VB6 is obsolete but after reading Creating Custom Controls for ms access 2010 VB6 seems to be easy choice. Any alternatives for writing OCX?
EDIT: Final working control is available here https://github.com/Combinatix/QRCodeAX
For 4. try setting your control's DataBindingBehavior to vbSimpleBound so that a scalar property (ControlSource in your case) can be bound via DataSource and DataMember properties.
For 3. use Tools->Procedure Attributes... menu, select ControlSource in Name, expand Advanced>> and select Data in Property Category combobox. You can do the same through Object Browser (F2). Find your control, right click your property/method (should be bold) and choose Properties... context menu option. This works with methods and is more versatile than Tools->Procedure Attributes... approach.
To answer your questions one by one:
Yes. ActiveX controls in Access are certainly different than in other Office applications.
In Access, there's one general CustomControl control that encapsulates all ActiveX controls, and offers a default set of events, properties and methods for any control, such as border properties, the requery method, and the enter event.
The object of the control being encapsulated can be accessed by using the CustomControl.Object proprty
These properties aren't displayed because you're referring to a custom control in the general sense, and only get the properties for it.
To get the object you want, use the following:
Dim qrObj As QRCode
Set qrObj = QR.Object
That's plain not possible, tab layout is determined by Access
Also not possible. The ActiveX control must include that functionality, this one doesn't. Generally, using ActiveX controls in continuous subforms to display something different for every row is hard to impossible
Resizing the outer control, and then calling CustomControl.SizeToFit should generally just work
Some of the things I deem not possible can be achieved by modifying the ActiveX Control's source code, of course.
A very different and much less cumbersome approach would be to generate the QR code online and download them to be displayed in a (bound) picture control.
I wrote an article on displaying online images:
Show pictures directly from URLs in Access forms and reports
Some code is, of course, needed, but much less than that at GiHub you refer to, though to much to list here in full.
It uses this function retrieve the images:
' Download (picture) file from a URL of a hyperlink field to a
' (temporary) folder, and return the full path to the downloaded file.
'
' This can be used as the control source for a bound picture control.
' If no Folder is specified, the user's IE cache folder is used.
'
' Typical usage in the RecordSource for a form or report where Id is
' the unique ID and Url is the hyperlink field holding the URL to
' the picture file to be displayed:
'
' - to a cached file where parameter Id is not used:
'
' Select *, UrlContent(0, [Url]) As Path From SomeTable;
'
' - or, where Id is used to create the local file name:
'
' Select *, UrlContent([Id], [Url], "d:\somefolder") As Path From SomeTable;
'
' Then, set ControlSource of the bound picture control to: Path
'
' 2017-05-28. Gustav Brock, Cactus Data ApS, CPH.
'
Public Function UrlContent( _
ByVal Id As Long, _
ByVal Url As String, _
Optional ByVal Folder As String) _
As Variant
Const NoError As Long = 0
Const Dot As String = "."
Const BackSlash As String = "\"
Dim Address As String
Dim Ext As String
Dim Path As String
Dim Result As String
' Strip leading and trailing octothorpes from URL string.
Address = HyperlinkPart(Url, acAddress)
' If Address is a zero-length string, Url was not wrapped in octothorpes.
If Address = "" Then
' Use Url as is.
Address = Url
End If
If Folder = "" Then
' Import to IE cache.
Result = DownloadCacheFile(Address)
Else
If Right(Folder, 1) <> BackSlash Then
' Append a backslash.
Folder = Folder & BackSlash
End If
' Retrieve extension of file name.
Ext = StrReverse(Split(StrReverse(Address), Dot)(0))
' Build full path for downloaded file.
Path = Folder & CStr(Id) & Dot & Ext
If DownloadFile(Address, Path) = NoError Then
Result = Path
End If
End If
UrlContent = Result
End Function
I pasted this URL into a record:
https://chart.googleapis.com/chart?cht=qr&chs=300x300&chl=23457
and it worked right away:
Full code can be found at GitHub: VBA.PictureUrl

store word content in variable

How do I copy the entire content (approx 2 pages) of a Word document in VBA and store in a variable?
I keep trying several things, none of which works:
Dim mainData As String
ThisDocument.Activate
ActiveDocument.WholeStory 'error on this line
mainData = Selection.Text
With 'record macro' I can simulate selecting a piece or the entire text, but I can't simulate storing that into a variable.
The above code throws
'This command is not available because no document is open',
but hadn't I first activated this (the current) document, and then selected it (ActiveDocument.WholeStory)?
Why doesn't this work?
Later edit: I managed to do the selection like this:
Dim sText As String
Application.Selection.ClearFormatting
Application.Selection.WholeStory
sText = Application.Selection.Text
MsgBox sText
but the problem is I can't store the entire text (2 pages) in a variable. Part of it is truncated. Would you know how to store word by word (I only need a word at a time anyway)?
Later edit. I applied strReverse on the text to find out the text is actually stored entirely in the variable, just not fully displayed in the message box.
Don't use ThisDocument in code, unless you specifically want to address the file in which the code is stored and running. ThisDocument is the "code name" of that file.
Instead, use ActiveDocument to mean the document currently active in the Word window.
An addition, if you want the Selection in the currently active document, there's no reason to activate it - it's already active.
So to get the entire document content in a string
Dim mainData As String
mainData = ActiveDocument.Content.Text
where Content returns the entire main body's text as a Range object.
Note: The MsgBox has an upper character limit. If you're working with long text strings and want to see what they hold the following has more (but not "infinite") capacity:
Debug.Print mainData
All you need is:
Dim mainData As String
mainData = ActiveDocument.Range.Text

Set MS-Access Image Control Source from Attached Image Using VBA

I'm adding a photo to form to display for each user with other info of that user. I created a tabbed form, on page one I select a user and press a button that runs the following code:
Private Sub Command106_Click()
Dim qry_rs As DAO.Recordset
Dim qry_db As Database
Set qry_db = CurrentDb
SQLString = "SELECT [Clients Info].* FROM [Clients Info] WHERE ((([Clients Info].Info_Client)='" & [Forms]![Form-1]![Combo13_PageOne_Name] & "'));"
Set qry_rs = qry_db.OpenRecordset(SQLString)
Forms![Form-1].[Info_Member].Value = qry_rs![Info_Member]
Forms![Form-1].[Info_Tower].Value = qry_rs![Info_ID]
End Sub
I tried using the same way that I set the values of that textboxes in setting an image control value
Forms![Form-1].[Info_Picture].Value = qry_rs![Info_UserIDPhoto]
But it's not working, can anyone help?
I receive this message:
Run time error 2465:
Microsoft Office Access can't find the field "|" referred to in your expression
I'm using Office 2007, on Windows 7
To my knowledge, there is no way to refer to the ControlSource property through vba. However, using the property sheet in Design View works PERFECTLY. Use the value of your combo box as the criteria in a DLookup expression.
Enter this as the ControlSource of your image control:
=DLookUp("[YourAttachmentField]","[YourTable]","[YourNameField] = '" & [YourComboBox]. [Column] (x) & "'")
(Make sure you're referencing the correct column; I'm just using x as an example.)
This won't work if you alter the attachment's name or location AFTER it's been attached, so if any changes are made you must reattach.
VBA allows you to set the Picture property:
Forms![Form-1].[Info_Picture].Picture=qry_rs![Info_UserIDPhoto]
The Picture property value must be a file name--or complete path and file name if not found in the default folder set in Access options.
When set from VBA, the value may be a statement or variable whose value is a (path and) file name. In the case above, the field [Info_UserIDPhoto] would contain the complete path and file name of each user's ID photo.
(Unlike the ControlSource property, you cannot enter a variable or field name for this property directly in the design view property page.)
You can reference the control source property by doing the following:
Dim c As Control
Set c = Forms("FormName").Form.Controls("ControlName")
c.Properties("ControlSource") = "Your control source name here"

One Central Header/Footer used by Multiple Docs (Word 2003 or 2007)

Inside Word (2003 or 2007), is there a way to have one Header/Footer that is used by Multiple documents?
I want to be able to change the header/footer in one spot and have it affect multiple documents.
i.e. I have 50 documents and they all have the same header/footer. Instead of opening all 50 documents to make the change, is there a way to link (OLE?) the 50 documents to a main document and only have to change the main document?
If there is not a built in way, has anyone done this using VBA?
I'm not sure how will this will work in practice, but you can insert other files into a Word document as a link.
First create the document with the header/footer content, with the content in the body of the document. Save it.
Then go to one of your 50 documents, go into the header/footer. Go to INSERT | FILE. Locate the first file, then click the little drop-down arrow next to the OPEN button in the Insert File dialog. From the drop-down, select INSERT AS LINK. The content should now show up in the document. If you click in the content, normally it will have a grey background, to indicate it's really a Word field.
Now when you change the first document, you can open the second document, update the field (click anywhere in it and hit F9) and the new content will be pulled in. You can also update fields programmatically pretty easy, or under TOOLS | OPTIONS | PRINT, there's a box to auto update the fields every time the document is printed.
AFAIK to alter a documents header (simply) must be done by having the document open. That said you have a few options. First if the documents are saved in the office XML format then you could open the files using the MSXML library and alter the data in the header. (Or any of the dozens of other ways to alter what is essentially a text file.) If the file(s) are still in the binary format you really only have one of two options. The first is to open the file via vba and alter the header via the document object model. The second would be to figure out the binary format (which is documented) and alter it using the VB6/VBA native binary IO (very non-trivial).
Unless I thought I could gain more time then I was going to lose writing code to alter the documents directly I would probably just loop through all the file in the folder, open them and alter them. As for storing the header somewhere... You could just put the header data in a text file and pull it in. Or keep a document template somewhere.
Here is a very trivial example:
Public Sub Example()
Dim asFiles() As String
Dim lFile As Long
Dim docCrnt As Word.Document
asFiles = GetFiles("C:\Test\", "*.doc")
For lFile = 0& To UBound(asFiles)
Set docCrnt = Word.Documents.Open(asFiles(lFile))
docCrnt.Windows(1).View.SeekView = wdSeekCurrentPageHeader
Selection.Text = "I am the header."
docCrnt.Close True
Next
End Sub
Public Function GetFiles( _
ByVal folderPath As String, _
Optional ByVal pattern As String = vbNullString _
) As String()
Dim sFile As String
Dim sFolder As String
Dim asRtnVal() As String
Dim lIndx As Long
If Right$(folderPath, 1&) = "\" Then
sFolder = folderPath
Else
sFolder = folderPath & "\"
End If
sFile = Dir(sFolder & pattern)
Do While LenB(sFile)
ReDim Preserve asRtnVal(lIndx) As String
asRtnVal(lIndx) = sFolder & sFile
lIndx = lIndx + 1&
sFile = Dir
Loop
If lIndx = 0& Then
ReDim asRtnVal(-1& To -1&) As String
End If
GetFiles = asRtnVal
Erase asRtnVal
End Function