Outlook macro icon click event - vba

I have a subroutine assigned to a button on the ribbon. All it does is show a form I'm using to simplify some email processing I need to do. This is a constant work in progress so I added a message box in the form's initialization asking if this is live or not. That changes colors and some functionality to help my testing.
I don't want to have to answer that message box every time I use it (sometimes 30 or 40 times a day) but I still want a clean way to get into my testing mode. I'd like to shift-click or ctrl-click the ribbon icon or something like that to trigger my testing mode.
The public sub is in a module that just shows the form.
Public Sub RunAutoReply()
frmAutoReply.Show
End Sub
Is there any way to do this? I'm on MS Office Pro Plus 2016.

I though I knew what was asked until I read the comments in the question.
This could demo the concept.
Option Explicit
' Choose a name almost impossible to be used elsewhere.
Public testMode_for_RunAutoReply_unique As Boolean
'
Public Sub toggle_testMode_for_RunAutoReply()
testMode_for_RunAutoReply_unique = Not testMode_for_RunAutoReply_unique
If testMode_for_RunAutoReply_unique = True Then
Debug.Print "In test mode"
Else
Debug.Print "In live mode."
End If
End Sub
Public Sub RunAutoReply()
If testMode_for_RunAutoReply_unique = True Then
Debug.Print "Safe to test."
Else
Debug.Print "Live!"
End If
End Sub

Related

Document_open macro in Word isn't triggered until VBA-code is changed (insignificantly)

I have a .docm file with a Document_Open-sub, which calls another sub to open a small userform - or it's supposed to trigger and open - but it doesn't...
If I make changes to the document and save it - it still doesn't work.
If I make insignificant changes to the VBA-code in the same document (like adding a space character outside a sub) and save it - it suddenly works.
Why does it not work all the time? - and what can I change to make it work?
Additional info (I have no idea if some of it might be relevant)
In most cases the document is downloaded from an internal website and saved locally.
The document we've tested with is saved locally in a location which is trusted in Word.
In cases - where the sub isn't triggered automatically - the sub can be run with ALT+F8 (but that's not what I want).
The functionality works for some user - and not for others.
The functionality in the Document_Open-sub depends on a value read in CustomDocumentProperties.
System: Win10 - Word MSO365 build 2110
Edit - code added:
Public Sub Subrutine1()
ThisDocument.CustomDocumentProperties("prop1").Value = " " 'resetting prop1
UserForm1.Show 'prop1 is set from the userform
End Sub
Private Sub Document_Open()
If ThisDocument.CustomDocumentProperties("prop1").Value = " " Then
Call Subrutine1
End If
End Sub
You could rename your Sub "Subrutine1" to "AutoOpen."
I would recommend the following code.
Private Sub AutoOpen()
Dim myForm As UserForm1
Set myForm = New UserForm1
myForm.Show
Unload myForm
Set myForm = Nothing
End Sub
I would suggest resetting the document property as part of your userform initialization unless you sometimes use it without resetting that.
I would suggest using a document variable rather than a document property.
I suspect you would be better served with a document template rather than a document. In that case, the macro would be named "AutoNew."

How to ask for confirmation before allowing a Word document to close?

I work with 2 different keyboard layouts (qwerty and azerty), and am constantly switching between the two using Alt-Shift. Sometimes when I reach for Ctrl-Z to undo, I get instead Ctrl-W which closes the document. Generally speaking I would in these situations get a Save Prompt, which would alert me to my mistake. However, I am now using OneDrive, with Autosave on the documents I'm working on. As a result, there is never this save prompt. The document is instantly closed and, worse, the undo that I was trying to effect is no longer present when the document is re-opened.
I would like to use VBA attached to the Normal Template, such that, on closing any document created with the VBA template, I will check to see if Autosave is turned on, and if it is, confirm before closing with a messagebox.
Obviously if the user doesn't confirm (e.g. chooses Cancel) then the document should not be closed.
Initially I put my code on Document_Close, but soon realised that here it's too late to Cancel the close. This is no good to me.
Then, based on some online code, I put my code in a class module (EventClassModule). In the ThisDocument module, I initiate the class, and activate the event handler.
My code doesn't ever seem to run, or in anycase, breakpoints don't kick in.
I'm a little unsure of where Normal stops and my normal document begins...
In ThisDocument of Normal Template, I have this VBA
Dim X As New EventClassModule
Sub Register_Event_Handler()
Set X.App = Word.Application
End Sub
Private Sub Document_Open()
Register_Event_Handler
End Sub
And I have a class module called EventClassModule with the following
Public WithEvents App As Word.Application
Private Sub App_DocumentBeforeClose(ByVal Doc As Document, Cancel As Boolean)
Dim res As VbMsgBoxResult
If Me.AutoSaveOn Then
res = MsgBox("OK to Confirm", vbOKCancel, "Closing Document")
If res = vbCancel Then
Cancel = True
End If
End If
End Sub
I would like a prompt to confirm saving, any time a document is closed and the autosave option is turned off.
The Document_Open event handler will only fire if the Normal template is opened via File | Open. To get code to respond when starting Word you need to use an AutoExec routine. Simply rename your routine as below. You can find further info on Auto macros here
Public Sub AutoExec()
Register_Event_Handler
End Sub
Here is an easy way, include a method like this:
Public Sub DocClose()
' Disables Ctrl + W (but not other close methods, e.g. Alt + F4, menus, [X])
' Your code here:
ActiveDocument.Close
End Sub
Basically there are some method names that are used internally by MS-Word, and if you include them in your code they will prevent the default action. If you leave the sub empty it will effectively disable Ctrl + W.
See here for more information: Reserved method names in MS Word VBA
Advantage 1 of this technique
If you use Public WithEvents App As Word.Application and your project gets reset for any reason, and fails to restart... App will be Nothing and none of the events will fire.
Advantage 2 of this technique
On the occasions that you actually want to close a document... with this technique you wont get bugged by a (soon to become) annoying confirmation message - just sayin ;-)

Show progress on UserForm without percentage update

I want to show progress while my program is doing everything it does, to know that it is working and not failed. Problem is, I don't use any index, For...Next, While, or anything I can use to call my progress-bar update.
The program:
opens 2 books
copies a bunch of rows from one to another and
refreshes all the data/graphics in many other sheets.
As I don't have a loop through all the rows (which was my main reference point for progress-bar), I'm not sure how I can show the progress.
Sorry if I can't be more specific but I'm not sure if I can share any of the code. If you need more information I could try to post some in here.
My progress-bar code is:
Public Sub UpdateProgressBar(PctDone As Single)
With ProgressForm
' Update the Caption property of the Frame control.
.FrameProgress.Caption = Format(PctDone, "0%")
' Widen the Label control.
.LabelProgress.Width = PctDone * (.FrameProgress.Width - 10)
End With
' The DoEvents allows the UserForm to update.
DoEvents
End Sub
Public Sub UpdateProgressText(txt)
ProgressText.Caption = txt
End Sub

How do I effectively create controls dynamically in Excel's VBA or How do I use Application.OnTime()?

I am working on a very large VBA project in Excel at my job. We are about 1500 lines of code for just one feature and have about a dozen more features to add. Because of this, I've been trying to break everything down so that I can keep code for each feature in separate places. OOP sucks in VBA... The problem being that these controls MUST have events fired. Of course, some events (like the TextBox_AfterUpdate event) are not available when you dynamically create controls. It's a bit convoluted because of everything that is going on, so I'll break it down the best I can:
I have a class module that represents a tab for a multipage control. When a user clicks on a tab, the Userform calls this class module and THERE I have the controls created dynamically. This way I can keep the code in that class module. I have a sub that I deemed as the "AfterUpdate" sub and put code that I needed to run there. Now the problem is to get that sub to be called at the appropriate time.
So what I did is to set up a Timer of sorts to check and see if the "ActiveControl" is said textbox. If it is not, we can assume that focus has left and we can raise that event. Here's the code I'm using:
An abbreviated version of the tab creation...
Private WithEvents cmbMarketplace As MSForms.ComboBox
Public Sub LoadTab(ByVal oPageTab As Object)
If TabLoaded Then Exit Sub
Set PageTab = oPageTab
Dim tmp As Object
Set tmp = PageTab.Add("Forms.Label.1")
tmp.Top = 6: tmp.Left = 6: tmp.Width = 48
tmp.Caption = "Marketplace:"
Set cmbMarketplace = PageTab.Add("Forms.ComboBox.1", "cmbMarketplace")
' LOAD OTHER CONTROLS '
TabLoaded = True
Start_Timer
End Sub
Then Start_Timer:
Public Sub Start_Timer()
TimerActive = True
Application.OnTime Now() + TimeValue("00:00:01"), "Timer"
End Sub
And the sub that is to be fired:
Public Sub Timer()
If TimerActive Then
' DO SOME RANDOM THINGS '
Application.OnTime Now() + TimeValue("00:00:01"), "Timer"
End If
End Sub
Does this seem like a reasonable approach to solving the problem I'm facing? I'm open to suggestions...
That's the first problem. This seems like a lot of work to accomplish this. (I'm working on getting visual studio, but I don't know if that's going to happen)
The above code will work but the "Timer" sub will not get raised at all. I get no errors if I just run the code. Everything is created, everything works as I would hope. However, if I step through the code, I eventually will get the following error:
Cannot run the macro "...xlsm!Timer". The macro may not be available in this workbook or all macros may be disabled.
Obviously neither of those suggestions are valid. Macros ARE enabled and the sub is in the same darn class module. I tried making it public, same problem. Tried "ClassModule1!Timer" to no avail. I'm at my wits end trying to figure this out. Thinking of having people write ALL this in the Userform or just giving up.
Does anybody have any suggestions on how to effectively break up large chunks of code? And does anybody have a clue why this sub will not run and seemingly cannot be found?
I understand that this is a confusing situation, so if you need more info or code examples or want to know why I have something set up the way I do, let me know.
Thanks!
Obviously neither of those suggestions are valid. Macros ARE enabled and the sub is in the same darn class module.
There's the problem: a macro cannot be in a class module. The message is entirely correct: VBA cannot see the Timer procedure, because it's not accessible.
A class module is a blueprint for an object, VBA (or any OOP language for that matter) can't do anything with a class module, without an instance of that class - i.e. an object.
Your timer callback needs to be a Public Sub in a standard module, so that it can be called directly as a macro. Public procedures of a class modules are methods, not macros.
Depending on what ' DO SOME RANDOM THINGS ' actually stands for, this may or may not require some restructuring.
1500-liner spaghetti code can be written in any language BTW.

Programming VBA in an Outlook form

I created my own Outlook form to use it as standard surface to enter certain orders instead of the normal message form. The creation, editing and sending works perfectly fine and in the next step I want to insert some code via VBA.
My problem is that I can´t access the objects of my form in the VBA editor. E.g. I want to show a message box when a certain checkbox is checked. According code would be:
Sub example()
If CheckBox1.Value = True Then
MsgBox("Checkbox 1 is checked.")
End If
End Sub
When I run the code I get the error that the object could not be found. The same goes for every other object, like textboxes or labels etc.
I guess the solution is pretty simple, like putting Item. or sth. like that in front of each object. But so far I wasn't able to find the solution.
I´m using Outlook 2010.
I know this is a year too late but you'll want to do something like this example below. It's kinda a work around but you can get whatever value was selected.
Sub ComboBox1_Click()
Set objPage = Item.GetInspector.ModifiedFormPages("Message")
Set Control = objPage.Controls("ComboBox1")
MsgBox "The value in the " & Control.Name & _
"control has changed to " & Control.Value & "."
End Sub
You should be able to get the value, just get a handle on the object you want using the Inspector
The following is an excerpt from here
When you use a custom form, Outlook only supports the Click event for
controls. This is a natural choice for buttons but not optimal for
controls like the combo box. You write the code by inserting it into a
form’s VBScript editor. You need to have the Outlook form open in the
Form Designer and click the View Code button found in the Form group
of the Developer tab.
Sub CheckBox1_Click()
msgbox "Hello World"
End Sub
The code page is fairly minimal with no syntax highlighting. I just tried this now and it does work. Dont forget to Publish your form to pick up the new changes.
I know this is almost 6 years late but, in VB and VBA, simply start with the form name. (And if that doesn't work, just keep going up a parent object and you'll get there.) So, your code becomes:
Sub example()
If MYFORMNAME.CheckBox1.Value = True Then
MsgBox("Checkbox 1 is checked.")
End If
End Sub
Of course, after typing "MYFORMNAME." you'll know if it will work because typomatic will kick in when the system recognizes "MYFORMNAME" after you hit the period.