I created a simple userform for testing, but one issue comes up and i have no idea how to resolve it.
This is the case:
When I open .xlsm file for the first time and close the userform and workbook, this closes both of them.
But when I continue to click the .xlsm file again, close the userform and the workbook, it (the userform) pops up again.
It seems the workbook doesn't close.
Then i need to close the userform again to close this workbook.
I would like help to understand why the userform comes up again?
The userform only has one listbox controls.
The following code is in a module:
Private Sub Auto_Open()
UserForm1.Show
MsgBox "Close2"
ThisWorkbook.Close
End Sub
The userform contains the following code:
Private Sub UserForm_Initialize()
Application.Visible = False
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
MsgBox "Close1"
End Sub
You could try to put this into the workbook code section as followed.
And not put it in a separate module.
Private Sub Workbook_Open()
Application.Visible = False
userform1.Show
MsgBox "Close2"
ThisWorkbook.Close
End Sub
you can remove the code for the user form initialize.
The thing is as its hidden you might open it twice cause you do cause it does not respond directly.
Is it possible to get the same effect using ThisWorkbook.Application.Visible = False but only for one Workbook. I mean, I'd like to limiting user interaction only to UserForm, but I need have an access to anothers workbooks. At the moment this function cause hide workbook, but after open some another excel file - all object from userform are not available.
Private Sub Workbook_Open()
ThisWorkbook.Application.Visible = False
Starter.Show modeless
End Sub
Thanks for your support.
Please, create a form, let us say "Starter", having (at least) a button ("btExit"), copy the next code in its code module and show it. If the form in discussion already has some code in Initialize and Terminate events, please add the next code lines, too:
Option Explicit
Private Sub btExit_Click()
Unload Me
End Sub
Private Sub UserForm_Initialize()
ThisWorkbook.Windows(1).Visible = False
End Sub
Private Sub UserForm_Terminate()
ThisWorkbook.Windows(1).Visible = True
End Sub
So, you can simple use workbook Open event in this way:
Private Sub Workbook_Open()
Starter.Show vbModeless
End Sub
So I have a macro I wrote that I want to run on close of the workbook.
Unfortunately, the only way to apparently do this is to place the macro in the ThisWorkbook module of the actual file as opposed to having it sit in the PERSONAL.XLSB.
This is not desirable for a few reasons:
The macro would have to be put into every workbook it needs to be run on--I have hundreds.
The workbooks would need to be saved as macro enabled which, in my experience, many email servers won't accept emails with macro enabled workbooks attached.
So ideally I would like to be able to run the macro from the PERSONAL.XLSB in just a general module.
Any suggestions about how this might be possible?
EDIT:
Per instructions at:
http://www.cpearson.com/excel/AppEvent.aspx
PERSONAL.XLSB
CExcelEvents class module
Private WithEvents App As Application
Private Sub Class_Initialize()
Set App = Application
End Sub
Private Sub App_WorkbookOpen(ByVal Wb As Workbook)
MsgBox "New Workbook: " & Wb.Name
End Sub
PERSONAL.XLSB
ThisWorkbook
Private XLApp As CExcelEvents
Private Sub Workbook_Open()
Set XLApp = New CExcelEvents
End Sub
Doesn't work if you try to open a different workbook. If you click on PERSONAL.XLSB in recent documents it will trigger the message.
Move this into another ThisWorkbook object for a specific workbook and it still only works on that workbook:
Private XLApp As CExcelEvents
Private Sub Workbook_Open()
Set XLApp = New CExcelEvents
End Sub
So even though the class module is in PERSONAL.XLSB, it appears you still have to put the above into the workbook you want it to run on it, which I think I would still require saving it as an .XLSM and would run into email filter issues.
For some reason this did start working with everything in PERSONAL.XLSB although I didn't change anything. Exciting, but would like to know why.
However, now that I am trying to change the example to actually work how I need it with BeforeClose. So I updated to the following:
PERSONAL.XLSB
CExcelEvents class module
Private WithEvents App As Application
Private Sub Class_Initialize()
Set App = Application
End Sub
Private Sub App_WorkbookBeforeClose(ByVal Wb As Workbook, Cancel As Boolean)
MsgBox "Closing the workbook."
End Sub
PERSONAL.XLSB
ThisWorkbook
Private XLApp As CExcelEvents
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Set XLApp = New CExcelEvents
End Sub
Back to what happened yesterday... will only trigger when you go to close PERSONAL.XLSB. One would think that since the PERSONAL.XLSB opens with all workbooks, it would trigger regardless, but it isn't. Again, saving in the ThisWorkbook object of the target workbook works, but isn't a solution due to having to save as an .XLSM and email filters.
You can look at Application Events, which allow you to hook into events at the Application level, using code which can be in your Personal.xlsb
Here's a good starting point: http://www.cpearson.com/excel/AppEvent.aspx
I have all macros stored in personal.xlsm and untill now I have used a standard filter to hide/show columns. The new feature I want to implement now is that each user can have their own filter if they would like to. So basically i look in a folder for a personal filter if it exist and if it does it use that filter instead of the standard one.
But my problem is that i want to load a personal filter on workbookOpen event and reset to standard filter on the beforeClose event. My question is if I can do this from personal.xlsm in a way? Or do I have to manually go through all 250 workbooks and add in thisworkbook module onOpen and beforeClose events to call my method createFilter and resetFilter?
Here is a link to personal.xlsm for those who are not familiar with that methodology
In personal.xlsm, create a class module named "Helper". Place the following code in it:
Option Explicit
Public WithEvents xlApp As Application
Private Sub Class_Initialize()
Set xlApp = Application
End Sub
Private Sub xlApp_WorkbookOpen(ByVal Wb As Workbook)
'Your code here
End Sub
Your code for loading a filter should go in the 'Your code here bit.
Add a standard code module and add this code:
Public MyHelper As Helper
Finally, in the Workbook_Open event of personal.xlsm, place this code:
Private Sub Workbook_Open()
Set MyHelper = New Helper
End Sub
Save personal.xlsm and restart Excel.
Here is the code in helper if someones needs it:
Private Sub xlApp_WorkbookOpen(ByVal Wb As Workbook)
Dim myPath As String
On error resume next
If InStr(UCase(ActiveWorkbook.Name), "PARTSLIST") And (InStr(UCase(ActiveWorkbook.Name), "FILTERPARTSLIST") = 0) Then
myPath = Left(ThisWorkbook.Path, InStrRev(ThisWorkbook.Path, "\") - 1)
If Dir(myPath & "\filterPartsList.xlsx") <> "" Then '
Call ColumnCreater.updateFilter
Else
Call ColumnCreater.filterCreationStandard
End If
End If
End Sub
Protecting workbook structure will prevent a user from deleting sheets. But how could I (using VBA) prevent a user from deleting a particular sheet I designate? I've seen examples where an active sheet is prevented from deletion by
Set mymenubar = CommandBars.ActiveMenuBar
mymenubar.Controls("Edit").Controls("Delete sheet").Visible = False
in its Worksheet_Activate event but that of course only works if the sheet is activated.
Is there a way to prevent a sheet from being deleted whether active or no?
For clarity: I'm fine with the user deleting some sheets, just not a couple of particular sheets.
So protecting workbook structure won't work.
As far as I can tell, it isn't possible to natively tag a single sheet as non-deletable; and there isn't an event that can be used to detect when a sheet is about to be deleted so the workbook can be protected preventively.
However, here is one potential workaround:
Protect workbook structure: this will, as you indicate, prevent all sheets from being deleted.
Create a "Controls" sheet. On this sheet, maintain a list of all sheet names (except those you don't want to be deletable).
If users want to delete a sheet, they will have to select its name on the Controls sheet (e.g. in a data validation drop-down menu) and press a "Delete" button. This button will call a macro that temporarily unprotects the workbook, deletes the selected sheet, and then reprotects the workbook.
Of course, the users will have to get used to this way of deleting sheets (as opposed to just right-click > Delete on the sheet's tab). Still, this isn't crazy complicated.
As for how to achieve #2 i.e. maintaining that list of sheet names, I suppose you could make use of a UDF like this one (must be called as an array formula):
Function DeletableSheetNames() As String()
Application.Volatile
Dim i As Long
Dim sn() As String
With ThisWorkbook
ReDim sn(1 To .Sheets.Count)
For i = 1 To .Sheets.Count
With .Sheets(i)
If .Name = "DataEntry1" Or .Name = "DataEntry2" Then
'Don't include it in the list.
Else
sn(i) = .Name
End If
End With
Next i
End With
DeletableSheetNames = sn
End Function
You cannot stop users to delete a particular sheet but you could use the Workbook_BeforeSave() event to prevent the workbook from being saved if a particular sheet is missing. The documentation on this event precisely shows how to allow saving a workbook only when certain conditions are met. See http://msdn.microsoft.com/en-us/library/office/ff840057(v=office.14).aspx
I can prevent a sheet from being deleted via the Worksheet_BeforeDelete Event as follows:
Private Sub Worksheet_BeforeDelete()
Call ThisWorkbook.Protect("password")
Call MsgBox("This sheet cannot be deleted.", vbExclamation)
End Sub
This protects all sheets from being deleted, however if you add some event code on the ThisWorkbook module like the following :
Private Sub Workbook_SheetActivate(ByVal Sh As Object)
Call ThisWorkbook.Unprotect("password")
End Sub
I will then be able to delete any other sheet as soon as it is selected.
Bear in mind, you will lose copy and paste functionality between pages due to the page unlocking when it is selected.
"there isn't an event that can be used to detect when a sheet is about to be deleted"
Since Office 2013, it is possible with the SheetBeforeDelete event.
I found this solution, similar to Dan's, on ExtendOffice.com. Put this code on the Worksheet's module:
Private Sub Worksheet_Activate()
ThisWorkbook.Protect "yourpassword"
End Sub
Private Sub Worksheet_Deactivate()
ThisWorkbook.Unprotect "yourpassword"
End Sub
When you activate the sheet in question, the whole workbook is protected, and the "Delete" option is grayed out. When you switch to any other sheet, the workbook is free again. It's subtle because you only notice the change when you go to the "safe" sheet.
Answer is by adding the following code to each of the protected sheets:
Private Sub Worksheet_Deactivate()
ThisWorkbook.Protect , True
Application.OnTime Now, "UnprotectBook"
End Sub
And the following to a Module:
Sub UnprotectBook()
ThisWorkbook.Unprotect
End Sub
Check https://www.top-password.com/blog/prevent-excel-sheet-from-being-deleted/ for credits accordingly.
The following disables the menu when you right click on tab. This stops the delete option being available.
Sub tab_rclick_off()
Application.CommandBars("Ply").Enabled = False
End Sub
The following turns the menu back on.
Sub tab_rclick_on()
Application.CommandBars("Ply").Enabled = True
End Sub
This option is simple, concise, prevents any issues with data entry/editing with protected sheets and can be called from anywhere in code, ie in conjunction with log on permissions can be given to some and not others etc. foremost yourself.
Maybe you could try to protect the structure of the workbook in SheetBeforeDelete.
See my example:
Private Sub Workbook_SheetActivate(ByVal Sh As Object)
ThisWorkbook.Protect Structure:=False
End Sub
Private Sub Workbook_SheetBeforeDelete(ByVal Sh As Object)
If Sh.Name = "Example" Then
ThisWorkbook.Protect Structure:=True
End If
End Sub
Here is another answer from mine base on the idea of #Jean-François Corbett
You can use 'Protect WB Structure' and Event 'Workbook_SheetBeforeDelete' to achieve this.
The result is that an dialog will pop up said "Workbook is protected and cannot be changed."
Private Sub zPreventWShDel(WSh As Worksheet, Protection As Boolean)
Dim zPassword As String: zPassword = ""
Dim zWB As Workbook
Set zWB = WSh.Parent
If Protection Then
zWB.Protect zPassword, Protection
Else
zWB.Protect zPassword, Protection
End If
'Stop
End Sub
Private Sub Workbook_SheetBeforeDelete(ByVal Sh As Object)
Call zPreventWShDel(Sh, True)
End Sub
Private Sub Workbook_SheetActivate(ByVal Sh As Object)
Call zPreventWShDel(Sh, False)
End Sub
Do not Call the code on Sheet Deactivation like below. Because it will deactivate it. Sequence of running is Event_SheetBeforeDelete -> Event_SheetDeactivate.
Private Sub Workbook_SheetDeactivate(ByVal Sh As Object)
Call zPreventWShDel(Sh, False)
End Sub
I created a different approach to this.
On the sheet(s) you want protected, add this code:
Private Sub Worksheet_BeforeDelete()
ThisWorkbook.Worksheets("Unprotect Workbook").Visible = True
ThisWorkbook.Worksheets("Unprotect Workbook").Activate
ThisWorkbook.Protect
End Sub
Create the ("Unprotect Workbook") sheet and make the visibility: xlSheetVeryHidden
on the "Unprotect Workbook" sheet add a button or shape that you can assign a macro to.
on the "Unprotect Workbook" sheet, add this code:
Sub unprotectThisWorkbook()
ThisWorkbook.unprotect
ActiveSheet.Visible = xlSheetVeryHidden
End Sub
Assign the sub you added, "Sub unprotectThisWorkbook()", to the button on the "Unprotect Worksheet" sheet
When you delete the sheet you protected, the workbook is protected and it takes you to the unprotect worksheet as a notice to the user and as a way to unprotect the workbook. Once the button is clicked, the workbook is unprotected and the "unprotect sheet" is hidden again.
This will work for any sheet you want to protect.