Making QRCode ActiveX Control for MS Access: Control Source Property - vba

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

Related

Is accessing the "IEEE new reference window" in Word with VBA possible?

With VBA, is it possible to open the window displayed when adding a new IEEE reference in Word (2013-2019), and access the fields' filled in values on closing it?
It's the window which asks for the publication type (book, journal...) and based on that presents different TextBoxes to be filled in (authors, title, publisher...)
Perhaps along these lines (i.e. not working code, just an outline):
' Declare some variables
Dim result As Long
Dim sourceCount As Long
Dim mySource As Source
' record the Source count pre-dialog
sourceCount = ActiveDocument.Bibliography.Sources.Count
' Set the Bibliographic Style to IEEE
ActiveDocument.Bibliography.BibliographyStyle = "IEEE"
' Display or Show the Create Source Dialog box
result = Dialogs(wdWordDialog.wdDialogCreateSource).Display
' .Show and .Display *should in theory * return different values
' depending on whther the user clicked OK, Cancel, the dialog's
' Close box etc.but on Windows Word the returned value
' always seems to be 0. On Mac Word that seems to work OK.
' So actually you might as well get rid of 'result' and use
' Dialogs(WdWordDialog.wdDialogCreateSource).Display
' so see if any new entries have been added
If ActiveDocument.Bibliography.Sources.Count > sourceCount Then
' There's a new entry. For the sake of argument, assume that the newest Source is the last in the list
Set mySource = ActiveDocument.Bibliography.Sources(ActiveDocument.Bibliography.Sources.Count)
' At this point, it probably helps to look at the XML of the new source, e.g.
Debug.Print mySource.XML
' Because the XML only contains the elements corresponding to the
' fields that the user filled in.
' Perhaps the Tag field is always present - I have not checked.
' You can try to retrieve the value of a particular field using e.g.
Dim author As String
author = mySource.Field("author")
' but if there is no author element in the XML, that will raise an error
; i.e. you'll probably need some On Error Resume Next handling.
' To use that approach you have to know the field names for every
' possible field in any type of IEEE source, and/or what fields are
' allowed in each type of source.
' Not sure the Word object model will help you there.
' **new material**
' All the possible field names and the corresponding XML
' element names are listed in a file called bibform.xml
' in the Microsoft Office program folder structure.
' On my Windows system, that's in
' C:\Program Files\Microsoft Office\root\Office16\1033\Bibliography
' On Mac, it's inside the app's package, e.g. the US English
' version is in
' Application/Microsoft Word/Contents/Resources/en.lproj
' There are different bibform.xml files for different
' (human) languages but the XML element names are the same
' for every language.
' So perhaps a better approach would be to parse the XML for
' the new Source and discover what fields are actually in there.
' I'm not going to try that here.
Set mySource = Nothing
End If
If it turns out that the most recently added Source is not always this one
ActiveDocument.Bibliography.Sources(ActiveDocument.Bibliography.Sources.Count)
then you might have to do something like
create a list of Sources prior to displaying the dialog box
display the dialog box
if there's a new Source, work out which one it is by comparing with the existing list
Without further testing, it's not even obvious that the new Source's Tag/Tag Name is unique.
Notes:
MacWord seems to work much the same way except as commented above.
ActiveDocument.Bibliography.BibliographyStyle = "IEEE" assumes you have
an unmodified Office/Word installation with the standard IEEE Style installed.

Can a Variable be stored in an Excel File that can not be accessed through Excel

I just discovered that in MS Word it is possible to store a Variable in a MS Word File that can not be accessed through the regular interface when running Microsoft Word.
Sub SetMyVariable()
Dim VARNAME As String
VARNAME = "HiddenVar"
ActiveDocument.Variables.Add VARNAME, "My special info"
End Sub
This gets saved in the XML Schema under word\settings.xml
I have tried using the ThisWorkbook Object in Excel, but it doesn't seem to have a Variable object that can be added like in word.
I want to know if there is something similar in Excel to store information/varialbes that get saved with the file.
PS: the closest thing I can think of (and use in codig) is a hidden named range.
You can try with the CustomXMLParts property of the Workbook which from the link seems a generic feature of Office products and available in Excel. Given you noted that a user would have to manually inspect the XML within the unzipped xlsx files then this seems to map to the Word Variables feature. The code sample just substitutes ThisWorkbook for ActiveDocument:
Option Explicit
Sub TextXMLPart()
Dim objXMLPart As CustomXMLPart
'add
Set objXMLPart = ThisWorkbook.CustomXMLParts.Add("<foo>bar</foo>")
'inspect
For Each objXMLPart In ThisWorkbook.CustomXMLParts
Debug.Print objXMLPart.XML
Next objXMLPart
End Sub
The accepted answer to this question (which focuses on Excel and vsto) states that:
Custom XML parts For an application-level add in, this is my preferred method of storing any application data that needs to be persisted in a saved xls file without ever being visible to the user.

Save file with name from form content

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.

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"

Word macros not running correctly when opened from PowerPoint action button

I have a Word template (suggestion from) which includes an autonew macro to insert a reference number at a book mark and an action button (Submit)which saves the resulting document with the reference number as part of the file name and closes Word. This works perfectly well when opening the template via Windows Explorer.
We also have a PowerPoint show with action settings hyperlinking to various documents. The link will open the above template OK but does not insert the reference number. Also when the 'submit' button is hit, the file saves as another template with the reference number included.
I am not sure if the issue is Word or PowerPoint-related. The code for the Word template is
Sub AutoNew()
REF = System.PrivateProfileString("L:\Local\Lab\Section - Support Services\Health and Safety\H&S Suggestions\Settings.Txt", _
"MacroSettings", "REF")
If REF = "" Then
REF = 1
Else
REF = REF + 1
End If
System.PrivateProfileString("L:\Local\Lab\Section - Support Services\Health and Safety\H&S Suggestions\Settings.Txt", "MacroSettings", _
"REF") = REF
ActiveDocument.Bookmarks("REF").Range.InsertBefore Format(REF, "000#")
End Sub
Private Sub CommandButton1_Click()
REF = System.PrivateProfileString("L:\Local\Lab\Section - Support Services\Health and Safety\H&S Suggestions\Settings.Txt", _
"MacroSettings", "REF")
ActiveDocument.SaveAs FileName:="L:\Local\Lab\Section - Support Services\Health and Safety\H&S Suggestions\Suggestion " & Format(REF, "000#.doc")
Application.Quit
End Sub
Any help or pointers would be appreciated as if it works I'd like to use for various other templates.
From the description, it's kind of hard to get an accurate idea of what's happening, but it SOUNDS like the the AUTONEW just might not get run in that particular combination.
You could verify this by using some logging or MSGBOX calls to see exactly what macros are being run, when.
Check the docs on Autonew here
http://support.microsoft.com/kb/211659
Sounds like it won't run if the macro is saved in Normal, which doesn't sound like the case here but it's worth noting.
You might also consider using the AutoOpen macro and checking other elements to make sure this is a brand new doc instead of one that's already been saved (like checking the content of the Document.Fullname property).