I want to add this code to ThisDocument in my Word .docm file.
Sub AutoClose()
ActiveDocument.Saved = False
End Sub
The code prevents the "Do you want to save?" dialog from appearing. I only want to add this however after a certain click event in the document, because I also have Save As and Save disabled, and if I add all three, then I wouldn't be able to save the document myself. (In fact, I can't add all three, for that reason.)
If I add the above code only to be active after the last click event in the document however it should be fine, since then I can still save changes I make, as long as I haven't clicked that trigger at the end yet. Preventing people from closing and getting the "Do you want to save?" dialog at the end is the most important to me.
is there a way to write the above code to ThisDocument when another click event fires? Or put another way, is there any way to have a click event put the above code into effect?
Here's the sub at the end that I'd want to have trigger the above code to (activate? Be written? Be enabled? Be uncommented?)
Private Sub formatSaveB_Click()
ActiveDocument.Bookmarks("mark3").Select
Selection.Delete
ActiveDocument.InlineShapes(1).Delete
With Dialogs(wdDialogFileSaveAs)
.Format = wdFormatFilteredHTML
.Show
End With
End Sub
So after that event happens, I want
Sub AutoClose()
ActiveDocument.Saved = False
End Sub
to be active in ThisDocument -- but not before that event.
Programatic access to the VBA project will need to be enabled if it isn't already. However, to add the sub you described after the other code runs add this code to your sub:
ThisDocument.VBProject.VBComponents("ThisDocument").CodeModule.AddFromString "Sub AutoClose(): ActiveDocument.Saved = False: End Sub"
That should do the trick. You may have to tinker a little, I can't test fully because I'm in an environment where programatic access to the VBA project is disabled, but that should do what it sounds like you want to do.
Slight edit: If you want to prevent the "Do you want to save changes you made ... " message when closing word you would need to do this:
ThisDocument.VBProject.VBComponents("ThisDocument").CodeModule.AddFromString "Private Sub Document_Close(): ActiveDocument.Saved = True: End Sub"
Which will prevent that message from displaying when closing the document.
I can't completely follow your logic but I think what you're asking is something like this:
ThisDocument module
Public event1 As Boolean
Public event2 As Boolean
Sub AutoOpen()
event1 = False
event2 = False
End Sub
Event Procedures
Private Sub formatSaveB_Click()
'// Event code here...
event1 = True
End Sub
Private Sub otherEvent_Click()
'// Event code here...
event2 = True
End Sub
Then in your final sub
Sub AutoClose()
'// Saved state only set to true if previous 2 events have been executed.
ActiveDocument.Saved = (event1 And event2)
End Sub
Set a flag in the event handler you want to disable the dialog, then test it when you exit. Note that you have to check for either the flag or if the document is already saved to avoid being prompted if the document actually has been saved but you haven't triggered formatSaveB_Click(). Assumes that formatSaveB_Click() is in ThisDocument.
In ThisDocument:
Private clicked As Boolean
Sub AutoClose()
ActiveDocument.Saved = clicked Or ActiveDocument.Saved
End Sub
Private Sub formatSaveB_Click()
ActiveDocument.Bookmarks("mark3").Select
Selection.Delete
ActiveDocument.InlineShapes(1).Delete
With Dialogs(wdDialogFileSaveAs)
.Format = wdFormatFilteredHTML
.Show
End With
'Set your flag here.
clicked = True
End Sub
Okay adding this line from MattB did the trick, after I discovered that his suggestion had a typo, and I deleted the extra letter :)
ThisDocument.VBProject.VBCompontents("ThisDocument").CodeModule.AddFromString "Private Sub Document_Close(): ActiveDocument.Saved = True: End Sub"
I was just pasting it in to try it like the others, and it had VBCompontents which I didn't spot until just now going over this again, and once I deleted the extra t, bingo.
Just what I needed. Thanks a bunch.
Related
I have a sub called as sub1() which activates the userform1 through the command userform1.show()
This userform1 has a button called as continue. On clicking that button continue - A Macro called as Private Sub continuebutton() gets activated.
I want to program in such a way that it redirects to the line after userform1.show() in sub1().
Is it something that can be done?
Theoretically, what you want is possible, if you do it like this:
In the UserForm:
Private Sub btnContinue_Click()
flag = True
Debug.Print "I continue ..."
sub1
End Sub
In a module:
Public flag As Boolean
Public Sub sub1()
If flag Then
Debug.Print "sub 1 continues here..."
Else
flag = False
UserForm1.Show
Exit Sub
End If
End Sub
It will work exactly as intended, BUT it is not a good practice to work this way. (Some people may throw stones at you for using public variables at all in VBA.) Here are two articles, that give better ideas:
https://rubberduckvba.wordpress.com/2017/10/25/userform1-show/
Disclaimer - this one is mine:
http://www.vitoshacademy.com/vba-the-perfect-userform-in-vba/
On the form properties for userform1, set its "Modal" property to true.
When the form opens, it will have exclusive focus, and the code in sub1 will not continue running until after it closes. This may be the solution you need.
In the code below, the msgbox will only appear once userform1 closes:
sub sub1()
userform1.show()
msgbox "Now continuing with sub1"
end sub
No way as long as you show the form.
If you show the form modal, the calling routine continues if (and only if) the form is closed.
If you show the form non-modal, the code continues to run directly after the show - so it's already done.
So either you have to close the form when the user clicks the "continue..." button to let the calling macro continue or you have to split your code into two routines and call the second on button-click.
You can change your Sub1 as follows:
Sub sub1(Optional Continue As Boolean)
If Continue = True Then
DoSomeStuff
Exit Sub
End If
userform1.show
End sub
And then, you can call your sub1 using:
Private Sub continuebutton()
Call sub1(True)
End Sub
Hope it helps
If you don't want to go with the 'Modal Form' solution, you could add a subroutine to your main module, and call it when required. So, in userform1, you have:
sub sub1()
userform1.show()
end sub
public sub sub2()
msgbox "Now continuing..."
end sub
And then in userform1, set some code on its onClose event:
Private Sub continuebutton()
Call sub2()
end sub
Working in VBA for excel 2010. This is my first time working with VBA and userforms. Right now I have a barebones userform "UserForm1" trying to sort this issue out. It consists of two radio buttons "OptionButton1" and "OptionButton2" belonging to GroupName "WK" and two textboxes "TextBox1" and "TextBox2".
When I run the userform, I want "OptionButton1" to be selected and the subsequent if/then statements to be run. However, right now I cannot get it to do this. My code:
Public Sub UserForm1_Initialize()
UserForm1.Show
Me.OptionButton1.Value = False
Me.OptionButton1.Value = True
MsgBox ("dia locked")
Me.TextBox1.Value = "blah"
End Sub
Public Sub UserForm1_Activate()
End Sub
Public Sub OptionButton1_Click()
If Me.OptionButton1.Value = True Then
MsgBox ("dia locked")
Me.TextBox1.Value = "blah"
End If
End Sub
Private Sub TextBox1_Change()
End Sub
When I run the form, nothing happens and "OptionButton1" is false. If I click it, the message box displays and the textbox displays the text. It just won't do it on startup. I have tried going into the optionbutton properties and setting 'value' to true, which makes it true on startup, but the messagebox still doesn't display and the textbox is blank until I click it.
...please help. Thank you.
I figured it out.
I suddenly found the dropdowns. Apparently I should have put Userform_Initialize() instead of UserForm1_Initialize(), and instead of OptionButton1_Click() I put the code into OptionButton1_Change() which ran the subsequent initialization sequence.
You guys/gals are awesomesauce. I have learned everything from reading your threads. Thank you!
Q: What is recommended way of notifing user about loss of functionality unless User change macro security settings?
What I do now:
I display warning on the first sheet user see after opening Workbook, with explanation why things WONT work unless proper settings are set.
And I hide it on start up. (Which wont happen unless settings are OK)
But its not perfect solution:
That message is just one time. (While user could send that sheet to somebody else with different settings...)
Hiding and showing those few rows is treaded by Excel as changing document. (So just opening and closing excel will generate Save changed warning!)
Ok, here is best answer I could think off
(And big thx to #brettdj for suggestion!)
Sub HideMacroSecWarning()
Status = ThisWorkbook.Saved
Help.Range("Warning").rows.hidden = True
ThisWorkbook.Saved = Status
End Sub
Sub ShowMacroSecWarning()
Status = ThisWorkbook.Saved
Help.Range("Warning").rows.hidden = False
ThisWorkbook.Saved = Status
End Sub
Those are for showing and hiding macros. Saved state is preserved so only user actions can trigger "unsaved changes" dialogbox.
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Call ShowMacroSecWarning
End Sub
Private Sub Workbook_Open()
Call HideMacroSecWarning
End Sub
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Call ShowMacroSecWarning
End Sub
Private Sub Workbook_AfterSave(ByVal Success As Boolean)
Call HideMacroSecWarning
End Sub
Those assure that no matter the state of user settings warning will ALWAYS be displayed in SAVED FILE. (So even if for user Exel do not display that warning, somebody else who will open it on different machine will see that warning.)
Previously I have used this:
Private Sub Document_Close()
Me.Saved = True
End Sub
to disable the save prompt when exiting a Word document and changes have been made, however I have added a "Combo Box (ActiveX Control)", and now Word is prompting to save again. Is there a way around this?
I've tried writing code to just delete the Combo Box when the document closes, but the box deletes itself after being used (not my code, it just does), then when the document closes the box doesn't exist and causes an error there. I could just do an if exist/error control, but I feel like that is just getting sloppy instead of finding the root problem... can someone help?
If had the same issue, and found the solution with Bing:
http://answers.microsoft.com/en-us/office/forum/office_2007-customize/activex-control-blocks-ms-word-vba/71eca664-8e43-4e4f-84c5-59154661ee5a
The following code helped me to get around this issue:
Dim WithEvents App As Application
Private Sub App_DocumentBeforeClose(ByVal Doc As Document, Cancel As Boolean)
'Did the user saved our file?
If Not ThisDocument.Saved Then
'Close our file?
If Doc.Name = ThisDocument.Name Then
'Cancel the dialog always!
Cancel = True
'Set the save state
ThisDocument.Saved = True
'Close the document after this event
Application.OnTime Now + TimeSerial(0, 0, 1), Me.CodeName & ".Document_AfterClose"
End If
End If
End Sub
Sub Document_AfterClose()
'Close the document without saving
ThisDocument.Close False
End Sub
Private Sub cboEquip_Change()
'Let the document know that a change is made
ThisDocument.Saved = False
End Sub
Private Sub Document_Open()
Set App = Application
End Sub
Say I have a button embedded into my spreadsheet that launches some VBA function.
Private Sub CommandButton1_Click()
SomeVBASub
End Sub
Private Sub SomeVBASub
DoStuff
DoAnotherStuff
AndFinallyDothis
End Sub
I'd like to have an opportunity to have some sort of a "cancel" button that would stop SomeVBASub execution at an arbitrary moment, and I'm not into involving Ctrl+Break here, 'cause I'd like to do it silently.
I guess this should be quite common issue, any ideas?
Thanks.
Add another button called "CancelButton" that sets a flag, and then check for that flag.
If you have long loops in the "stuff" then check for it there too and exit if it's set. Use DoEvents inside long loops to ensure that the UI works.
Bool Cancel
Private Sub CancelButton_OnClick()
Cancel=True
End Sub
...
Private Sub SomeVBASub
Cancel=False
DoStuff
If Cancel Then Exit Sub
DoAnotherStuff
If Cancel Then Exit Sub
AndFinallyDothis
End Sub
How about Application.EnableCancelKey - Use the Esc button
On Error GoTo handleCancel
Application.EnableCancelKey = xlErrorHandler
MsgBox "This may take a long time: press ESC to cancel"
For x = 1 To 1000000 ' Do something 1,000,000 times (long!)
' do something here
Next x
handleCancel:
If Err = 18 Then
MsgBox "You cancelled"
End If
Snippet from http://msdn.microsoft.com/en-us/library/aa214566(office.11).aspx
Or, if you want to avoid the use of a global variable you could use the rarely used .Tag property of the userform:
Private Sub CommandButton1_Click()
Me.CommandButton1.Enabled = False 'Disabling button so user cannot push it
'multiple times
Me.CommandButton1.caption = "Wait..." 'Jamie's suggestion
Me.Tag = "Cancel"
End Sub
Private Sub SomeVBASub
If LCase(UserForm1.Tag) = "cancel" Then
GoTo StopProcess
Else
'DoStuff
End If
Exit Sub
StopProcess:
'Here you can do some steps to be able to cancel process adequately
'i.e. setting collections to "Nothing" deleting some files...
End Sub
what jamietre said, but
Private Sub SomeVBASub
Cancel=False
DoStuff
If not Cancel Then DoAnotherStuff
If not Cancel Then AndFinallyDothis
End Sub
I do this a lot. A lot. :-)
I have got used to using "DoEvents" more often, but still tend to set things running without really double checking a sure stop method.
Then, today, having done it again, I thought, "Well just wait for the end in 3 hours", and started paddling around in the ribbon. Earlier, I had noticed in the "View" section of the Ribbon a "Macros" pull down, and thought I have a look to see if I could see my interminable Macro running....
I now realise you can also get this up using Alt-F8.
Then I thought, well what if I "Step into" a different Macro, would that rescue me? It did :-)
It also works if you step into your running Macro (but you still lose where you're upto), unless you are a very lazy programmer like me and declare lots of "Global" variables, in which case the Global data is retained :-)
K
~ For those using custom input box
Private Sub CommandButton1_Click()
DoCmd.Close acForm, Me.Name
End
End Sub
This is an old post, but given the title of this question, the END option should be described in more detail. This can be used to stop ALL PROCEDURES (not just the subroutine running). It can also be used within a function to stop other Subroutines (which I find useful for some add-ins I work with).
As Microsoft states:
Terminates execution immediately. Never required by itself but may be placed anywhere in a procedure to end code execution, close files opened with the Open statement, and to clear variables*. I noticed that the END method is not described in much detail. This can be used to stop ALL PROCEDURES (not just the subroutine running).
Here is an illustrative example:
Sub RunSomeMacros()
Call FirstPart
Call SecondPart
'the below code will not be executed if user clicks yes during SecondPart.
Call ThirdPart
MsgBox "All of the macros have been run."
End Sub
Private Sub FirstPart()
MsgBox "This is the first macro"
End Sub
Private Sub SecondPart()
Dim answer As Long
answer = MsgBox("Do you want to stop the macros?", vbYesNo)
If answer = vbYes Then
'Stops All macros!
End
End If
MsgBox "You clicked ""NO"" so the macros are still rolling..."
End Sub
Private Sub ThirdPart()
MsgBox "Final Macro was run."
End Sub