DoCmd.CancelEvent / Cancel = True Usage - vba

Can either of DoCmd.CancelEvent or Cancel = True be run with effect from a procedure called by an event procedure whose event can be cancelled? E.g.,
Private Sub mCbo_DblClick(Cancel As Integer)
DoThis
End Sub
Private Sub DoThis()
Foo
{ Cancel = True | DoCmd.CancelEvent }
Bar
End Sub
I.e., will the Cancel = True / DoCmd.CancelEvent statement in DoThis() cancel the mCbo.DblClick event or is the statement effective only in the event procedure itself? The former would seem to be in scope in a broad sense, at least, but this isn't clear.
Secondarily, wherever the statement must appear, will subsequent code execute? I.e., will DoThis() execute Bar or will the statement effectively operate as Exit Sub, immediately ceasing any further execution?
This would seem straightforward but an hour or two Googling around hasn't turned up anything on point. Many thanks for any constructive thoughts.

You can easily do this by passing the cancel parameter by reference:
Private Sub mCbo_DblClick(Cancel As Integer)
DoThis Cancel
End Sub
Private Sub DoThis(ByRef Cancel As Integer)
Foo
Cancel = True
Bar
End Sub
And, of course, Bar is still ran. You could use Exit Sub if you want to prevent it from running. The Cancel parameter is just an integer that's used after all event handlers have ran to determine if the default behaviour should be cancelled. Setting it in a procedure doesn't do anything immediate.

Related

VBA Automation Error - "server [not server application]" upon closing form

I've tried creating a form in the object-oriented manner as shown in this answer: https://stackoverflow.com/a/38382104/4460023. Upon closing the form, I'd like to refer to the object property IsCancelled to check if the calling subroutine should continue executing. However, when I check this property outside of the form, I run into the following error:
"Run-time error '-2147418105': Automation error. The callee (server
[not server application]) is not available and disappeared; all
connections are invalid. The call may have executed."
I'm guessing this has something to do with the form being closed. As an alternative solution, I simply write to a global variable stored within the calling sub's module. Ideally though, I'd like to use the property within this form object. My code is included below:
Within the form:
Private cancelling As Boolean
Public Property Get IsCancelled() As Boolean
IsCancelled = cancelling
End Property
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode = VbQueryClose.vbFormControlMenu Then
cancelling = True
End If
End Sub
And then within the calling subroutine:
Set frm = New ViewByWorkerForm
frm.Show
If frm.IsCancelled Then 'error happens here
Exit Sub
End If
Note that I have other string properties within the class that I can use when I do not close the form - it's only the form closing that triggers this problem.
To fix your code
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode = VbQueryClose.vbFormControlMenu Then
Cancel = True
End If
Hide
cancelling = True
End Sub
For me, I don't have a "red x" (I hide it) and I didn't want to use Booleans. I call my UserForms like so via an Event:
Public Sub UserForm_Sub()
'Application.ScreenUpdating = False
Set UserFormUserForm = New UserFormUserForm
On Error GoTo ErrorHandler
UserFormUserForm.ShowUserFormUserForm
GoTo Continue
ErrorHandler:
ModelessFormShowing = False
Continue:
End Sub
It was a simple matter of utilizing Excel's Error Handler in my case. You might be find w/ On Error Resume Next, but in my case, I needed to set a ModelessFormShowing boolean to false if form errored during launch.

VBA - UserForm Button (Macro) to Continue running the sub

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

Cannot interrupt running operation in vba when command button is being pressed

I researched net, but I haven`t found a solution yet and I am still grappling with the following problem:
In vba UserForm I have two command buttons:
1st one ('Run Operation') runs an operation that could take around 30 minutes.
2nd one ('Cancel') was created to interrupt the operation that was triggered off by 'Run Operation'
When I press 'Run Operation' button I cannot press neither 'Cancel' nor 'x' to stop the running operation and I have to wait until the operation finishes, thus the userform is freezed for around 30 minutes.
Code looks more or less like this:
private Sub Cancel_Click()
Cancel = True
End Sub
private Sub RunOperation_Click()
RunOperation.Enabled = False
Call Macro()
End Sub
private Sub Macro()
For i = 1 to 100
'do stuff here
If Cancel = True Then
RunOperation.Enabled = True
Exit Sub
Exit If
Next i
End Sub
What`s more both buttons have TakeFocusOnClick set to False.
I`d be grateful for any ideas.
Thanks in advance !
The DoEvents method is your friend here.
What's happening is that since VBA is single-threaded (i.e. only one macro can be running at a time) it's not possible for events (in your case Cancel_Click()) to trigger. The DoEvents method essentially pauses the code wherever it appears to see if any other events have been triggered and resolves them before code execution is resumed.
Try this, it should work:
Private Sub Macro()
For i = 1 To 100
'do stuff here
DoEvents '<~~ Insert this line here
If Cancel = True Then
RunOperation.Enabled = True
Exit Sub
End If
Next i
End Sub

VBA EventHandler firing twice?

I have MS Access 2003 DB.
Is it possible for an event handler for a button on a form to fire twice??
I seem to have evidence of this happening as I have a payroll process
that logs the whole process and process is duplicated in the log.
I didnt think this was possible in VBA???
EDIT:
I discovered that indeed it was firing twice as user was clicking twice and queueing the event twice.
This is the fix I made to the code which shows using a flag m_locked as an example to test with:
[code]
Private m_locked As Boolean
Private m_count As Integer
Private Sub Command0_Click()
On Error GoTo Err_Command0_Click
' wait
If Not m_locked Then
m_locked = True
Dim startTime As Date
startTime = Now()
While DateDiff("s", startTime, Now()) < 3
DoEvents
Wend
' increment counter
m_count = m_count + 1
Command0.Caption = m_count
m_locked = False
End If
Exit_Command0_Click:
Exit Sub
Err_Command0_Click:
MsgBox Err.Description
Resume Exit_Command0_Click
End Sub
[/code]
Malcolm
Seeing your "solution" I'ld recommend to specify the double click event, too. This will allow you to distinguish easily whether the user clicked once or twice by a "debug.print".
To prevent the user to perform an extra click, declare a private boolean variable on form module level, set it to TRUE in your event procedure, set it to FALSE in the timer event, and configure your form's timer to 1000 for example (it's milliseconds).
Option Explicit
Option Compare Database
Private oneClick As Boolean
Private Sub cmdMyButton_Click()
If not oneClick Then
' Perform your actions here
End If
oneClick = True
End Sub
Private Sub Form_Timer()
oneClick = False
End Sub
Oh, and please use variable and control names that tell their meaning :-)
If you do not want your user click the button twice just this simple code:
Private Sub Command0_Click()
Command0.Enabled = False
' Continue with your code here ...
End Sub

How to stop VBA code running?

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