Word VBA: Make macro easy to run - vba

I've made a form-letter document with a macro that performs the mail merge. I don't want the user to have run it from the menus, and I want this to be portable. If there's a way for a button to appear on each user's ribbon or quick command menu, I'm not familiar with it.
So I put a button in the document itself. Unfortunately, every form-letter created has the same button in it. I suppose I could write the code to delete every one, but I think that would be slow.
Is there a way to assign a shortcut key to an existing macro, and have it reside in the document?

I had to implement something pretty similar to what you were referring to some 10 staff. My solution (by no means as portable as desired) was to export the macros and forms from my Normal template to the other users, I coupled this with Ribbon customization and it worked well. Unfortunately, when a change was needed, I had to trudge over to everyone's machine individually.
I would suggest you stick with your solution of deleting button after the merge is complete. Here's some code to help with that:
Sub DeleteCommandButton()
For Each o In ActiveDocument.InlineShapes
If o.OLEFormat.Object.Name = "CommandButton1" Then
o.Delete
End If
Next
End Sub
Good luck, hopefully this helps.

Related

Auto-updatable links

Is there a way to apply "auto-updatable" style for hyperlink?
I believe, this question is not trivial.
When you normally click on hyperlink, it will change it's color to violet. Next, if you save, close, and then reopen the document, the link will be updated back to blue. This is default behaviour of Word, and there is no need to use any macros for it.
I'm trying to replicate this behaviour with VBA. Here is the code:
Sub Test1()
Selection.Range.Hyperlinks(1).Range.Fields(1).Result.Style = Word.WdBuiltinStyle.wdStyleHyperlinkFollowed
End Sub
To make it work, simply put caret into the link, run macro, and see the results:
This works fine, except such visited links will not be auto-updated after you save, close, and then reopen the document. See the difference in the picture below. The link "Google" was opened normally, using the mouse Ctrl-click; the link "StackOverflow" was opened using the macro:
As I already said, I want to make my VBA-opened links (StackOverflow) auto-updatable as well (as Google).
Yes, I understand, there is a workaround - simply create another macro, which will be started every time the document opened and change all violet hyperlinks back to blue. However, this is just workaround, and I don't like it. Using it, we use conversion from "permanent violet" to "permanent blue", instead of using "temporary violet" (that's mean, auto-updatable without any additional efforts).
Hope everything is clear. Thanks in advance.
Update (was added after several answers were already posted).
Yes, I understand, this will work:
Sub Test1()
On Error Resume Next 'To avoid an error in case if the link isn't reachable
Selection.Hyperlinks(1).Follow
End Sub
But I want just simulate following, without really opening the link in the browser. That's why, I can't use Selection.Hyperlinks(1).Follow.
you need to remove the line, the link will change once followed and change back once the doc is reopened.
Selection.Range.Hyperlinks(1).Range.Fields(1).Result.Style = Word.WdBuiltinStyle.wdStyleHyperlinkFollowed
this does it for me
Sub resetHyperlinks()
Dim hLink As Hyperlink
For Each hLink In ActiveDocument.Hyperlinks
hLink.Address = hLink.Address ' this works
' hLink.ScreenTip = hLink.ScreenTip ' this works also
Next hLink
End Sub
You don't need to change the style with code to make the link purple. Just use the Follow method. This will click the link and turn it purple and then it will be reset to blue upon opening the document again.
Sub Test1()
Selection.Range.Hyperlinks(1).Follow
End Sub
You can reset link styles with VBA code that runs at startup, i.e. is a part of Document_Open() routine in ThisDocument VBA module.
The Hyperlink class doesn't have any .Visited property or anything relevant (i.e. you cannot even see if it was visited), so there's no other way beside .Follow() that also opens the link as it should.
You're basically trying to falsify the information that the document provides about its state: make a link appear visited when it actually wasn't.
The fact that the class doesn't even provide a property means that Word's designers do not consider the visited status a part of the editor's functionality (i.e. it effectively doesn't exist as far as the program's job is concerned).
This evidence suggests that Word doesn't, and is not designed to, have any specialized facility to switch link status other than .Follow(). Which means, any way that you find that happens to have the desired effect in bound to be what you're calling a "workaround".
The "temporary" color of a followed hyperlink is an embedded (and not directly accessible) feature of the built-in Hyperlink character style. It is not exposed through the normal UI's Style tools, nor through the object model.
You can readily compare all formatting between two selections using the Reveal Formatting pane (Shift+F1) in the document window in Word.
If you compare a normally followed hyperlink with a hyperlink affected by your snippet, you'll see that the followed hyperlink still has the Hyperlink style, while your simulated follow has changed the style of the second hyperlink.
If you compare a never-followed hyperlink and a normally followed hyperink, Word identifies their formatting as exactly the same. Word does not acknowledge that any aspect of formatting (style, font color, etc.) has changed.
It seems likely that the Word.WdBuiltinStyle.wdStyleHyperlinkFollowed you are using exists explicitly to address this gap (which is somewhat disappointing).
I recommend using your existing approach, and then reverting the style in a procedure triggered by the Before Save and Before Close events of the document. Using those events will prevent the followed style from saving at all, and so avoid issues caused by someone opening the document without enabling macros.
Option Explicit
Sub test()
Dim HL As Hyperlink
For Each HL In Sheet1.Hyperlinks
HL.Range.Style.Font.Color = vbBlue
Next
End Sub
Can't you simply make it any colour you want without invoking it. As others have stated above whatever you you I think will be a work around as it's not an intended function.

MS Word RibbonX How can I dynamically populate a combobox when a dotm file opens?

I'm a little stumped here. I'm diving deeper into designing ribbons for MS Word 2010, and I came across something new: populating comboboxes on the fly. In the image below, you can see...
...that I'm a dude who likes music while he works, just like any other dude. Problem is my list of playlists changes from time to time, so I don't want to hard-code that list into my ribbon's combobox. I can easily hard-code it, but I want this thing to be dynamic. And so, in my ribbon code:
<comboBox id="cmbPLaylist" label="Playlist" getItemLabel="Document_Open">
<item id="none" label="None"/>
</comboBox>
I have left only one item, "none," which is fine if I want the music player to launch with no playlist loaded. But what if I want a playlist to automatically load?
First, from my Google and book research, I've determined that I need to have a getItemLabel callback to populate the control. Is this the right way to go? But how do I run that automatically when my Normal.dotm loads? I'm having problems running this thing in the Document_Open event, and I've been reading online that I'm not alone.
My problem is a bit threefold: first, I'm really new at using these predefined callbacks like getEnabled, getItemLabel, etc. The callback territory is a very new territory for me. Second, I've never used a combobox in a ribbon before. Three, I've never dynamically populated a combobox in a ribbon before. I might be trying to bite off more than I can chew at once, but can anyone point me in the right direction?
My code so far, inserted into my Normal.dotm Document_Open event, is such:
Private Sub Document_Open(control As IRibbonControl, ByRef label)
Dim ListOfPlaylists() As String
ListOfPlaylists = GetPlaylists()
ListOfPlaylists(UBound(ListOfPlaylists)) = "Random"
End Sub
After this, I'm stumped. As you can see, I'm not sure how to tell MS Word, "Hey, MS Word, insert this value into the combobox list!"
Maybe it's my newbness at this whole thing, but when I Google for an answer, I'm not seeing it in the code. So any help is appreciated. Thanks!
I actually did some fiddling and almost stumbled on the answer. I put this in my code and it seems to work just fine now:
Sub drpPlaylists_getItemCount(Control As IRibbonControl, ByRef drpPlaylists_itemCount)
drpPlaylists_itemCount = UBound(ReadDirectoryContents(MusicDirectory, "*.m3u"))
End Sub
I guess this gets launched every time the ribbon has to reload itself. But it's solved for now. Still have some things to study up on on when these callbacks get called, but I'll figure this out. Thanks for the help!

Prevent Word macro in Normal.dotm for some templates

I have a Normal.dotm file that contains an AutoNew macro.
This macro is automatically executed each time a new document is created using any other template.
Is there any way I can prevent this automatic behavior for a specific template?
I have a Word VSTO add-in running, so I can hook into Word's events, but so far I havn't found a way to prevent this.
I do know that I can prevent macro execution when using templates programmatically, for example like this:
' Disable auto-macros before opening document
wordApplication.WordBasic.DisableAutoMacros(1)
' Open document
newWordDocument = wordApplication.Documents.Open(template.FullName, ConfirmConversions:=False, [ReadOnly]:=True, AddToRecentFiles:=False, Revert:=True)
' Re-enable auto-macros
wordApplication.WordBasic.DisableAutoMacros(0)
But this solution doesn't work when the user uses a Word template from Windows explorer or the Open-dialog in Word, since in those cases I can't execute code before it's too late already.
Or can I?
I hope someone has a trick for me :-)
-
Edit: While trying different solutions, I discovered something that might help others in similar situations, though unfortunately it doesn't help me.
It seems that if a template contains a module containing an AutoNew (or AutoOpen for that matter), that local macro is executed instead of the one in Normal.dotm.
Example:
Normal.dotm contains the following macro:
Sub AutoNew()
MsgBox "Normal.dotm"
End Sub
Test.dotm contains the following macro:
Sub AutoNew()
MsgBox "Test.dotm"
End Sub
When executing Test.dotm the message "Test.dotm" is displayed, while the message "Normal.dotm" is not displayed.
If the AutoNew macro is removed from the Test.dotm template, the message "Normal.dotm" is indeed displayed.
So it is possible to easily override the auto-macros.
The local versions of AutoNew and AutoOpen can even be empty subs that do nothing. It still works.
This is not possible in my case though, since the template I use is generated by code, and cannot contain macros (because adding macros to templates programmatically requires the user to manually activate the option "Trust access to the VBA project object model", and that's something I cannot ask my customers to do for all users. It's also a security risk.)
Based on the workaround described in the "Edit" part of the question - providing a template with "empty" Auto-macros - it's possible to use the Open XML SDK to create a template and add the VBA project to it in order to provide this functionality. This approach avoids the user needing to allow access to the VBA project on his installation. The only "macro security" that could be triggered is that for not allowing macros to run. But since the client uses macros, anyway, this should not be a major obstacle.
The simplest method is to create as much of the basic template as possible in the Word UI and use this as a starting point.
Since you're unfamiliar with the Open XML SDK, the next step would be to create one (or more) templates in the Word UI using the basic template as the starting point, saving under a different file name.
You can then use Open XML SDK Productivity Tool to see the code required to generate any one of these files, as well as, using the Compare tool, the code for converting the basic template to the derived version. This should give you a decent start with the SDK and it's object model. Once you get a feel for how the Open XML SDK works, if you're familiar with Word's object model, using that provided by the SDK is relatively straight-forward as an effort was made to make it correspond as closely as possible to the "COM" object model.
The VBA project can be added later, but you can also include it in the basic template. That would be the simplest approach.
Include this "starting point" basic template as part of your solution, installing it as part of the solution.
Within the AutoNew macro you can check the AttachedTemplate property. Only if it is a template where you want to apply the cleaning you can execute the respective macros.
Sub AutoNew()
If ActiveDocument.AttachedTemplate <> "Normal.dotm" Then
Exit Sub
End If
' rest of the macro
End Sub
If you don't control the Normal.dotm you can put an empty AutoNew macro in your own templates. As Word only executes the auto macro in the closest context, the macro in the Normal.dotm file would not be executed.
If you don't control the other templates either, you can tell your users to hold down the SHIFT key while creating a document. This prevents the execution of the auto macro.
Probably it is best, however, if you ask the owner of the other system to find another solution that does not rely on polluting the Normal.dotm file.

VBA Macro changing macro

Is it possible to create a macro in MS Office (in this case Word) that will change other macro code? I was trying to find information but no results.
I have a doc which works as a template. Content of template is changed and then saved to another file. However it is important to have current date in it. It cannot be self-updated. Those docs go to folder of people and it is important to know when they get the document, so it must be simply data (or something that does not update).
I was thinking about an on-start event macro that would input current date and on exit it would ask "Do you want self-update functionality" Yes / No
If Yes, delete that event. However I have no idea if it is possible. If it is I still don't know how to search for it.
No this is not possible. In VBA, unlike some lower level languages when you define an event you can not disable it, even using other VBA code.
In C# or VB.NET, Java or C++ you can disable an event by un-wiring it from the handler, but this is not possible in VBA.
Maybe if you be more clear on what you need I can give you a better answer.

How to update an Access VBA app with 30 forms?

I need to update an Access VBA app with around 30 forms in it.
I have to amend a screen that seems to have been set up right at the start of the app, it uses a lot of SQL tables. Is there an way of finding my way to the start of the code?
I come from a procedural coding background and I am unused to code that doesn't have a start and an end; I also know a bit of VB, some ASP, some .Net and general computing.
When something "automagically" happens upon opening an Access database, it is almost always because
A "startup form" has been specified. (In Access_2010 that's done in File > Options > Current Database > Display Form.) ...or...
The database has a Macro named AutoExec which is automatically run when the database is opened (unless you bypass it by holding the [Shift] key down while opening).
In addition to #Gord's answer, there's a few things you need to know. I'm going to give you the quick & dirty version.
First, there's 2 types of code in Access. VBA & macros. Sometimes what's called a macro, is really VBA.
In Access, a macro is a set of instructions to do something to the database. It's very limited in what it can do. These are often used by novices who don't know how to program in VBA.
VBA is the real powerhouse behind the scenes. It can do everything a macro can do, but a whole lot more.
Access uses an Event-Driven / Object-Oriented (at least close enough for this discussion) interface. Do a Google search on those meanings. But very quickly, the listbox on a form is an object. It has properties (like width), methods (add an item), and events (click on an item).
To see the code, for macros look to to your navigation window to your left. For VBA (modules), look to the same window, or just press Alt-F11. VBA can be used standalone in a module, or behind the scenes of a form or report.
Once you get the hang of it, you'll find Access to be a handy RAD tool for small projects.
Good luck.
It appears that you already have found the form that opens when the app starts (if not, check out Gord Thompson's answer).
The first things that happen when an Access Form opens (the "start of the code", as you called it) are the Load and Open events.
If there is any code in this form that is connected to these events, then it's in the Form_Load() and Form_Open() functions in the code of the form.