Event for BeforeSave(Save As) event on previous document - excel

I have some kind of "warning" to avoid an Excel sheet is being used without macros enabled.
In my example it's a plain stupid rectangle on top of my first sheet that asks the user to enable his macros.
When you turn the macros on the rectangle is hidden, when the document is closed it's visible again.
Private Sub Workbook_Open()
ThisWorkbook.Worksheets("sheet 1").Shapes.Range(Array("Rectangle 1")).Visible = False
End Sub
Private Sub Workbook_BeforeClose(Cancel As Boolean)
ThisWorkbook.Worksheets("sheet 1").Shapes.Range(Array("Rectangle 1")).Visible = True
End Sub
Code works well, until the document is "saved as", the event in the BeforeClose is not triggered while saving as.
There is the "BeforeSave" event where you have a boolean value if it's "Save as" or not, but this event will be triggered on the new document that was saved, not on the old / previous one.
Problem is when I open the old document, the rectangle is not visible, because it was hidden while opening, but not set visible again through the "BeforeClose" event.
Any idea how to solve this ? I should call this code on the "previous" document before it's saved as or something like this.

Related

How to prevent a Boolean value from being reset?

360 separate sheets make up this workbook.
The first sheet is the "Main Page," which is opened when the workbook is first opened, and has a userform that pops up containing the instructions for the main page.
For about 350 of the other sheets, there is a copy & pasted userform which contains instructions for those sheets, which all operate in the same manner.
Within those 350 userforms, there is a toggle button that flags a public variable as true, which prevents them from popping up again when the sheet is opened.
I want that toggle button to prevent those userforms from ever popping up again as long as the button is depressed on one of those pages.
If sheetOpenned = False Then
SubPageInstructions.Show
End If
sheetOpenned = True
End Sub
sheetOpenned is the public boolean declared at the top of the page. Is there a way to make this flag as true for every page?
The public Boolean is reset when the workbook is closed.
How can I
prevent the public Boolean from being reset?
prevent the toggle button in my Userforms from being auto toggled to their default value on workbook close?
Just for the record, I leave here the solution using the Windows Registry, registering the value of ToggleButton1 in it. In this example, I used a CommandButton, where the code evaluates whether there is a negation to display the Userform, written to the Registry. In the Userform itself, the code for registering the value of ToggleButton1 is saved, once clicked. The "VB & VBA Program Settings" subkey exists for each logged in user. So, for a new logged in user, the Userform will be displayed and, if the ToggleButton1 is clicked, it will be disabled for future reopenings of the Workbook.
On the Userform code pane:
Private Sub ToggleButton1_Click()
If Me.ToggleButton1.Value = True Then
'don't show Me again.
Me.ToggleButton1.Value = False
fToggleButton1_State = False
SaveSetting "My350Sheets", "SavedState", "ToggleButton1", False
Else
'uncomment the line below if you want the Userform to be shown again in the *NEXT* WB_open event
'SaveSetting "My350Sheets", "SavedState", "ToggleButton1", True
End If
End Sub
On a standard Module code pane:
Option Explicit
Public fToggleButton1_State As Boolean
On a CommandButton code:
Option Explicit
Private Sub CommandButton1_Click()
If fToggleButton1_State = True Then
UserForm1.Show
End If
End Sub

Non modal UserForm and Workbook navigation

Very specific problem not related directly to code but more to Excel behavior.
When launching a non modal UserForm (vbmodeless) from a minimized WorkBook (not visible on screen), while another WorkBook is on screen, Excel will "link" this UserForm and its focus to the visible one. This means that minimizing or maximizing the second WorkBook will hide and show the UserForm, but doing so with the first WorkBook won't, even if it has been launched by the first WorkBook.
Worse, closing the second WorkBook will close the UserForm.
An example of the issue :
Sub TestSo()
Application.WindowState = xlMinimized
UFTest.Show vbModeless
End Sub
Just create an empty UserForm named UFTest, open 2 Workbooks and display them split mode on screen and then call this Sub. You'll see that the UserForm is now linked to the wrong WorkBook.
Is there any way to prevent this from happening?
Does it do the trick?
Sub TestSo()
UFTest.Show vbModeless
ThisWorkbook.Windows(1).Visible = False
End Sub
Then in Userform restore the visibility
Private Sub UserForm_Terminate()
ThisWorkbook.Windows(1).Visible = True
End Sub

Excel VBA Start Userforms Modeless and then go Modal

Excel allows to start with a Modeless form and then display a Modal (but not the other way around)
I have an app with 4 Userforms : UF1 - Select a partner, UF2 - List existing transactions, UF21 - Display an existing transaction, UF22 - Make a new transaction. UF21 and UF22 both stem from UF2.
UF21 needs to be Modeless in order to display more than one transactions and compare side by side, therefore UF1, UF2 and UF21 are all Modeless. But I want UF22 to be Modal in order to issue one new transaction at a time.
My problem is that after I close UF22, even just ESCaping from the form right off the bat, all previous forms close. I should be able to return to UF2. If I make UF22 Modeless all is ok.
I have written a function to traverse the UserForms Collection and I am able to get a reference to the object of the Form I want to activate. So, I am able to return (in debug mode) to UF2 which is a listbox, activate the list box, but after the last pending statement both UF2 and UF1 close.
Is what I am trying to do impossible due to the nature of the Modal and Modeless forms or should I keep pushing for the correct code?
Since my original question is still open and my tested implementation of the proposed solution by #PeterT is not working properly, I include the code I have for the moment, based on #PeterT 's suggestion.
'===============
' Form UF1
'===============
Private Sub UserForm_Activate()
If ActivateUF22(FormID) = True Then Exit Sub
'.... more commands
End Sub
'============
' Form UF2
'============
Private Sub UserForm_Activate()
If ActivateUF22(FormID) = True Then Exit Sub
'.... more commands
End Sub
'----------------
Private Sub Cbn_OpenUF22_Click()
If ActivateUF22() = True Then
Exit Sub
Else
With New UF22
.Show vbModeless
End With
End If
End Sub
'================
' In a Module...
'================
Public Function ActivateUF22() As Boolean
Dim frm As Object
Set frm = GetFormFromID("UF22*") ' Custom function to get a form Object based on
' some criterion (FormID in a hidden TextBox)
If Not frm Is Nothing Then
' the only way I know to *Activate* an already .Show(n) form and compensate
' for the fact that the Close CommandButton may already have Focus
frm.TBx_UF22_CODE.SetFocus
frm.CBn_UF22_CLOSE.SetFocus
ActivateUF22 = True
Else
ActivateUF22 = False
End If
End Function
Well I finally managed to get the workaround to behave.
The remaining problem was the fact that clicking twice in a row on the same userform, besides the "Modal" one, would succeed and allow the user to break out.
I even tried the "AppActivate Application.caption" approach found in another SO thread but that didn't work either.
The only solution that works and does not bother me is to insert a MsgBox with a warning to the user, as such:
Public Function ActivateUF22() As Boolean
Dim frm As Object
Set frm = GetFormFromID("UF22*") ' Custom function to get a form Object based on
' some criterion (FormID in a hidden TextBox)
If Not frm Is Nothing Then
' the only way I know to *Activate* an already .Show(n) form and compensate
' for the fact that the Close CommandButton may already have Focus
frm.TBx_UF22_CODE.SetFocus
frm.CBn_UF22_CLOSE.SetFocus
ActivateUF22 = True
MsgBox("You cannot move away from this form until it is either completed or cancelled")
Else
ActivateUF22 = False
End If
End Function
Displaying the MsgBox does the trick internally, switches the focus to a different form from the one clicked and, upon return, the UserForm.Activate event fires normally and the ActivateUF22 function prevents the user from escaping the Pseudo-Modal form.
Thanks #PeterT for pointing me to a workaround. I managed to do what I set out to do, albeit in a different manner.
PS I still believe that there is a way to switch from a Modeless form to a Modal one. After all the MsgBox I use is obviously a Modal form and works just as I would like ;-)

how to check if closing the excel (workbook) vba

I'm looking for a code that can return a boolean 0,1 or true or false, so that it can recognise if someone closes the excel by clicking on X
Open the VBA Editor, then go on ThisWorkbook (in the project tree on the left-side of the IDE) and you will access the code of the workbook events.
Then, from the dropdown list on the right-top select the event BeforeClose after having selected the Workbook object in the left-top dropdown menu, and you will get the following code on the module:
Private Sub Workbook_BeforeClose(Cancel As Boolean)
End Sub
That macro will be called every time someone tries to close the Workbook.
There, you can put a global variable to control the action, something like:
someoneIsClosing = True '<-- this is a globally defined variable
Cancel = True '<-- if you want to cancel the closing action
myMacro '<-- if you want to go back to "myMacro", i.e. any macro of your project and delegate it from handling the event.

Excel VBA: Save As triggers Change event in ComboBox

I have an Excel workbook containing some ComboBox controls placed directly on the sheets. These are standard combo boxes from the Forms toolbar.
When the user uses "Save As" to save the workbook with a different name, this triggers the Change event on all the combo boxes, including ones on sheets that aren't active. This seems unreasonable as the selection hasn't actually changed. This causes various undesirable behaviour because of the code in the event handlers. The event isn't triggered on a simple "Save".
Google suggests this is a known problem in Excel. There are rumours that it's caused by using a named range as the ListFillRange for the combo box, which I have done, although it's not a volatile name. I'm looking for a way to prevent this happening with minimal changes to the code and the spreadsheet. Does anyone have a proven solution?
I did the following in a new workbook with only one sheet, Sheet1, and it seemed to work to diable events before save and then reenable them after. It should bypass the problem you see by mimicing an AfterSave event. This is my event code on Sheet1 (could be OLEObject code, too)
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
MsgBox Target.Address & ": " & Target.Value
End Sub
This is my ThisWorkbook code
Option Explicit
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
' To see the change code work before disabling
' Should show a message box
Sheet1.Range("A1") = "Before After Events Off"
Application.EnableEvents = False
Application.OnTime Now, "ThisWorkbook.Workbook_AfterSave"
' This time it will not show a message box
' You will never see this one . . .
Sheet1.Range("A1") = "After After Events Off"
End Sub
Private Sub Workbook_AfterSave()
Application.EnableEvents = True
End Sub
The .OnTime method throws the AfterSave "event" onto the execution queue. It works!
You could set a flag in the Workbook's BeforeSave event and then check that flag before processing a change event in each of the combo boxes. There does not seem to be an AfterSave event, so you would need to clear the flag after checking it within the combo box change events. The flag would need to be more than a simple boolean since it could not be turned off until all combo box change events were processed. Here's some sample code:
Public ComboBoxChangeCounter As Integer
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Const NumOfComboBoxChangeEvents As Integer = 5
ComboBoxChangeCounter = NumOfComboBoxChangeEvents
End Sub
Function JustSaved() As Boolean
If ComboBoxChangeCounter > 0 Then
ComboBoxChangeCounter = ComboBoxChangeCounter - 1
JustSaved = True
End If
End Function
Private Sub Combo1_Change()
If JustSaved Then Exit Sub
'Your existing code '
' ... '
' ... '
' ... '
End Sub
I set the number of combo box change events as a constant, but there may be some way for you to determine that number programmatically. This workaround does require adding code to every combo box change event, but it should be easy as all you need to do is copy and paste the line If JustSaved Then Exit Sub at the beginning of each event.
This workaround assumes that the Workbook BeforeSave event will get called prior to the combo box change events. I don't know for a fact if that's the case.

Resources