I have an odd thing (I think) but probably doing something incorrectly here. I am trying to force the user to enter in certain information in excel. The msgbox comes up with the OK/Cancel buttons but when either is selected the workbook saves and exits. If they cancel then I want it to sit on the cell where the issue is. Any help would be appreciated
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
'---------------------------
'-- Missing Employee Name --
'---------------------------
If Cells(5, 2).Value = Empty Then
If Not MsgBox("Employee Name missing! Pressing OK will exit without saving.", vbOKCancel, "The Mill") = vbOK Then
With Sheets("sheet1")
.Activate
.Cells(5, 2).Activate
End With
Cancel = True
Exit Sub
End If
End If
End Sub
I think the problem is that you aren't handling Workbook_BeforeClose, which triggers as you try to close the workbook.
So while the save is canceled, the close is not.
Moving everything to Workbook_BeforeClose and handling it there kind of works, until the user saves manually. So you will still have to handle that in the before save event.
This is an example:
Private Sub Workbook_BeforeClose(Cancel As Boolean)
If Cells(5, 2).Value = Empty Then
If MsgBox("Employee Name missing! Pressing OK will exit without saving.", vbOKCancel, "The Mill") = vbOK Then
Application.DisplayAlerts = False
Application.Quit
Else
Sheets("Sheet1").Cells(5, 2).Activate
Cancel = True
End If
End If
End Sub
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
If Cells(5, 2).Value = Empty Then
Cancel = True
MsgBox "Employee Name missing!"
Sheets("Sheet1").Cells(5, 2).Activate
End If
End Sub
The only caveat I have seen, is that Application.DisplayAlerts = False will affect other open workbooks.
Related
I have code for Workbook_BeforeClose event. I like how it works now but I have just noticed a problem with Application.Visible = False. When I click Yes, it saves Workbook, when I click No, it does nothing, but when I click Cancel it already done Application.Visible = False and I can't see Excel application. How to fix that?
Private Sub Workbook_BeforeClose(Cancel As Boolean)
On Error Resume Next
Application.Visible = False
Application.DisplayFormulaBar = True
ActiveWindow.DisplayHeadings = True
ActiveWindow.DisplayGridlines = True
ThisWorkbook.Unprotect Password:="123456"
ActiveWorkbook.Sheets("Start").Visible = True
ThisWorkbook.Worksheets("Start").Activate
ThisWorkbook.Protect Password:="123456", Structure:=True, Windows:=False
End Sub
Instead of relying on the built-in dialog, try using your own. That way you have more control of what happens and when.
So maybe something like:
Dim closing As Boolean
Private Sub Workbook_BeforeClose(Cancel As Boolean)
If Not closing Then
answer = MsgBox("Save data?", vbYesNoCancel + vbQuestion, "Save data?")
If answer = vbYes Or answer = vbNo Then
closing = True
' your code here
ActiveWorkbook.Close savechanges:=answer = vbYes
Else
Cancel = True
End If
End If
End Sub
I have vba code below to edit every row one by one on userform interface:
Private Sub CommandButton1_Click()
Dim MyRange As Range
Dim cell As Range
Set MyRange = Range("A2", Range("A2").End(xlDown))
For Each cell In MyRange
cell.Select
'Build form
MyForm.Title.Caption = cell.Offset(0, 2)
If cell.Offset(0, 2) = 1 Then
MyForm.Checked.Value = True
Else
MyForm.Checked.Value = False
End If
'Show form
MyForm.Show
Next cell
End Sub
I have thousand of rows and I need to give user a some break!
I want this process to pause if user clicks X button, but I couldn't find a way to exit from the loop.
My userform code is like below:
Private Sub Checked_Click()
If MyForm.Checked.Value = True Then
ActiveCell.Offset(0, 1).Value = 1
Else
ActiveCell.Offset(0, 1).Value = ""
End If
End Sub
Private Sub DoneButton_Click()
ActiveCell.Offset(0, 2).Value = 1
Unload MyForm
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode = vbFormControlMenu Then
MsgBox "paused"
End If
End Sub
What I'm trying to accomplish?
I need to stop for each loop at first block of code when users click X button to close the userform. But I don't know how to listen if UserForm_QueryClose called to exit loop from first block of code.
Sadly I don't think this is possible, given vba doesn't support multi-threading.
If you really insist on asking the user whether to pause, it would be better to simply ask an If conditional during the run-time.
eg.
For i = 1 to myRange.Cells.Count
' your code here...
If i = myRange.Cells.Count / 2 Then
If MsgBox("Continue to the for loop?", vbYesNo) = vbNo Then
UnLoad MyForm
Exit For
End If
End If
Next i
You need to add DoEvents which will check for a user interaction. However, simply closing the form will not stop the code from running so you will need to add another controller.
For example
Option Explicit
Private stopCode As Boolean
Private Sub UserForm_Click()
Dim i As Long
stopCode = False
For i = 1 To 1000000
Debug.Print i
DoEvents
If stopCode = True Then Exit For
Next
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If MsgBox("Do you want to stop", vbQuestion + vbYesNo) <> vbYes Then Cancel = True
stopCode = True
End Sub
When the user closes the form they will be asked if they want to stop the code. If they say yes it closes the form but the stopCode 'controller' also makes sure the running code is stopped.
I already have a successful "custom save" macro to save-as with a date stamp. I just want to have a message box ask to run it when someone tries to manually save. I essentially need "yes" to run the macro, "no" to save normally, and "cancel" to exit sub.
However, whenever I file>save, or ctrl+s, it just saves without prompting.
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Dim answer As VbMsgBoxResult
answer = MsgBox("Would you rather Save-As copy with date stamp?", vbYesNoCancel + vbQuestion + vbDefaultButton1, "You are overwriting the document!")
If answer = vbYes Then
Call filesave
ElseIf answer = vbNo Then
ActiveWorkbook.Save
Else
Exit Sub
End If
End Sub
You need to set the Cancel from the sub procedure's arguments to True in order to halt the current save operation.
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Dim answer As VbMsgBoxResult
answer = msgbox("Would you rather Save-As copy with date stamp?", vbYesNoCancel + vbQuestion + vbDefaultButton1, "You are overwriting the document!")
If answer = vbYes Then
'Cancel the current standard save operation
Cancel = True
Call filesave
ElseIf answer = vbNo Then
'don't do anything; the standard save operation will proceed
Else
'Cancel the current standard save operation
Cancel = True
End If
End Sub
I've hijacked saving in an excel file for the purposes of preventing unintended overwrites of a file in an odd environment.
The Workbook_BeforeSave and the Workbook_BeforeClose events work perfectly on their own. Unfortunately, the way the code is currently structured, I would need to call the BeforeSave event from within the BeforeClose event.
In the most basic form, the following code will not do as I'd like. In the following example, wksHiddenWorksheet.Visible = True will not make wksHiddenWorksheet visible if the save is called from within the BeforeClose event.
Option Explicit
Private Sub Workbook_BeforeClose(Cancel As Boolean)
ThisWorkbook.Save
End Sub
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
wksHiddenWorksheet.Visible = True
End Sub
Is there any way around this? Is it poor practise (or even fundamentally incorrect) to call an event from inside another event in the way that I'm attempting?
Update:
I have played around with Application.EnableEvents in the workbook, but I've made sure it always reverts back to True in error handling.
I've opened a brand new workbook and entered the following code
Option Explicit
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Debug.Print Application.EnableEvents 'Prints TRUE
ThisWorkbook.Save
End Sub
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
'The workbook consists of two sheets. Sheet1 and Sheet2
Sheet1.Visible = xlSheetHidden
Debug.Print Sheet1.Visible 'Prints -1 (xlSheetVisible)
End Sub
The Visible sheet does not become hidden, though the line is triggered if I go line by line through the code. If I enter code such as a MsgBox in the same space, the MsgBox will open as normal.
Update 2:
To answer everyone's questions and display exactly what happens, please see the below code:
Option Explicit
Private Sub Workbook_BeforeClose(Cancel As Boolean)
'The workbook consists of two sheets. Sheet1 and Sheet2
Debug.Print Application.EnableEvents 'Prints TRUE
Debug.Print Sheet1.Visible 'Prints -1 (xlSheetVisible)
Sheet1.Visible = xlSheetHidden
Sheet1.Visible = False 'Same thing
Debug.Print Sheet1.Visible 'Prints 0 (xlSheetHidden)
Sheet1.Visible = xlSheetVisible
Sheet1.Visible = True 'Same thing
Debug.Print Sheet1.Visible 'Prints -1 (xlSheetVisible)
ThisWorkbook.Save
End Sub
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Debug.Print Sheet1.Visible 'Prints -1 (xlSheetVisible)
Sheet1.Visible = xlSheetHidden
Sheet1.Visible = False 'Same thing
'Sheet1 should now be hidden, but it's not
Debug.Print Sheet1.Visible 'Prints -1 (xlSheetVisible)
End Sub
Hiding and unhiding sheets works perfectly in the first event, but as soon as the second event is triggered, the sheet visibility does not change.
Update 3:
Another point. If I enter the Workbook_BeforeSave() event directly (by saving), rather than entering it from the Workbook_BeforeClose() event, then everything works as expected.
MUCH LATER UPDATE:
Although I marked this question as solved a while ago, I've come across an article that helps to explain the behaviour. An explanation can be found at:
http://www.cpearson.com/excel/events.aspx
edited after all clarifications
the following code worked for me
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Workbook_BeforeSave False, True
ThisWorkbook.Save
End Sub
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
If Cancel Then Sheet1.Visible = xlSheetHidden
End Sub
hard to tell why though...
In this example procedure HideSheet is called from BeforeSave and from BeforeClose event handler. So both handlers hide the sheet. HTH
Private Sub Workbook_BeforeClose(Cancel As Boolean)
HideSheet Sheet1
ThisWorkbook.Save
End Sub
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
HideSheet Sheet1
End Sub
Private Sub HideSheet(wks As Worksheet)
wks.Visible = xlSheetHidden
End Sub
I found this code and it works perfectly. But when I hit close button, dialog is shown "Do you weant to save changes", and if I choose Yes, an error comes up that I don't have a value in a cell A. And then my file is automatically closed.
How to prevent this, and to stay in the document?
My code is:
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
If ActiveSheet.Range("A1").Value = "" Then
Cancel = True
Response = MsgBox("Please enter a value in A1", vbCritical, "Error!")
End If
End Sub
Have you tried using BeforeClose instead of BeforeSave?
Private Sub Workbook_BeforeClose(Cancel As Boolean)
If ActiveSheet.Range("A1").Value = "" Then
Cancel = True
Response = MsgBox("Please enter a value in A1", vbCritical, "Error!")
End If
End Sub
Regards