Good day,
I am using Excel 2013 and I would like to hide and unhide my Sheets as I work with them. I spent some time Googling around and found plenty of ancient posts on forums about adding VBA to modules, but that's not quite what I'm looking for.
On a main page where I spend most of my time using data, I have a button that shows a UserForm with a list of sheets in a ListBox. I choose the Sheet from the ListBox, hit OK, and it runs the following;
Private Sub OKButton_Click()
ThisWorkbook.Sheets(JobListBox.value).Visible = xlSheetVisible
ThisWorkbook.Sheets(JobListBox.value).Activate
Unload Me
End Sub
I would like it so when I have my new sheets created via VBA, I can populate the sheet with the following subroutine;
Private Sub Worksheet_Deactivate()
Me.Visible = xlSheetVeryHidden
End Sub
If anyone can let me know how I can make a Subroutine to insert this code into my sheets, I would greatly appreciate it.
PS: My fallback method is to, of course, just copy/paste the code manually... But I would prefer to automate it if possible.
Instead of adding the same code to each sheet, since they are all inside the workbook and you are really trying to execute a hide once any sheet in the workbook is deactivated, put this in the code for ThisWorkbook.
Private Sub Workbook_SheetDeactivate(ByVal Sh As Object)
Sh.Visible = xlSheetVeryHidden
End Sub
You might be able to use more workbook events with your type of project.
Here is a list of the workbook events.
If you want to exclude your main page from this, you can modify this by adding an IF statement:
Private Sub Workbook_SheetDeactivate(ByVal Sh As Object)
IF Sh.Name <> "Main" Then
Sh.Visible = xlSheetVeryHidden
End If
End Sub
The main line of thinking being that if you have to put the same code into more than one object, let alone ALL of them, you are repeating yourself.
Check out the concept of DRY, or "Don't Repeat Yourself", unless you like it WET, "We Enjoy Typing". or "Write Everything Twice". Even if it's just going to be created programmatically, a chunk of code shouldn't have to exist in all your sheets exactly the same when you can have one piece of code have an incoming argument that is a worksheet.
This way, if you have to make a change to its behavior, you do it once. It's easily testable and less to keep track of or modify later.
So if you find yourself having to use the same code over and over, look to the parent object and try to find a way to pass the changing object or variable through as an argument to a singular piece of code, or module.
Also, this is probably why you aren't finding any results on inserting the same code into every sheet. It's not a good practice
Article on DRY
Related
I made a workbook with several sheets for data entry, and I want the index/main sheet to display upon opening every time.
Everything on the internets says to use something like below, but I get a runtime error.
I've made sure the worksheet is named "Attendance Main" and the spelling is correct.
Most troubleshooting issues I've seen are about activating sheets for copying/pasting and other actions. I just want the main sheet to display each time the workbook is opened.
When I create a new workbook, with 2 basic sheets, the macro works. So I am wondering if other macros are interfering with it. However, none of my macros are in "ThisWorkbook", most are in their own modules.
Any help appreciated, and if you need any info from me to narrow down the issue I will try to provide what I can.
Private Sub Workbook_Open()
Worksheets("Attendance Main").Activate
End Sub
Run-time error '1004'
Activate method of Worksheet class failed
For me a simple select code inside the open event of Workbook works fine.
Private Sub Workbook_Open()
Sheets("ShowThisFirst").Select
End Sub
I've been self-teaching myself VBA for a couple of years now (working on it on and off to make my work life easier). I'm usually able to find answers to my questions on this forum, but not this one.
I have a lot of 'example' code in my own personal.xlsb. For instance I have a public sub with a modified messagebox with standard caption etc I always like to make visible, referred to as PublicSub_MyMsgBox
Often, the automation I create, will be used by my colleagues as well. I would create a specific workbook with specific buttons for a specific goal. I would copy the required code into a module in this shared workbook. The workbook would therefore have a module with the sub PublicSub_MyMsgBox as well.
Another sub in this shared workbook will have a line to call PublicSub_MyMsgBox.
How can I be sure that when I test a code created in this shared workbook, it does not use the PublicSub_MyMsgBox from my own personal.xlsb, but actually only uses the code from the workbook?
(to be able to verify that I have indeed copied all relevant code into the workbook for my colleagues to use as well).
Thank you very much for your help!
Linda
No code from personal.xlsb will run even if a reference to pers. workbook exists! You can be sure that no code from personal.xlsb will be run, instead of the one inside the workbook where the call is made.
Even if you added a reference to your personal.xlsb, for identic macro names VBA will choose the one in the workbook where the code runs. To check that, please, try the next scenario:
Create in your personal workbook the next macro:
Public Sub MyMsgBox()
MsgBox "Hello from Personal workbook!"
End Sub
Then create the next macro in a standard module of your new workbook:
Sub MyMsgBox()
MsgBox "Hello from This workbook!"
End Sub
Create a simple checking Sub, of course, in the new workbook, add a reference to your personal.xlsb workbook and run it:
Sub testMyMsgBox()
MyMsgBox
End Sub
It will run the Sub in your workbook. Now, comment the Sub from your workbook and run testMyMsgBox again. It will ran the procedure from personal.xlsb...
I have a workbook with many worksheets. Some of them must be protected, and some of them unlocked. The tricky part is that a userform created using Excel VBA will be used to add data to one of the protected worksheets, and the code fails when said worksheet is protected because it is supposed to add rows to a table to save the data.
I tried using the following code to fix this:
Sheets("Sales").Protect Password:="123", AllowInsertingRows:=True, UserInterFaceOnly:=True
Although after running it I can manually add rows through the user interface, it seems that the property I am trying to use only works that way, VBA code still can not and new rows to the protected worksheet.
So, the workaround I thought of is to unprotect the worksheet when the userform is initialized, like this:
Private Sub UserForm_Initialize()
Sheets("Sales").Unprotect "123"
End Sub
This way the userform can save all the data without any issues. However, my question is how to properly protected it again. There are two ways of closing the userform, one is with a cancel button that just runs the Unload Me piece of code, and the second one is using the X on the top right corner of the form. I would like to keep those two options available, but also make sure that the worksheet is protected again before the user can try to edit it (it is imposible to select anything with the userform open).
What I am thinking would be the best option is the following code:
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
Sheets("Sales").Protect Password:="123"
End Sub
After testing it I believe it works, but I am not sure if this is the proper way of doing this. Maybe there is a specific situation that I am not considering that would end up with the user being able to edit the worksheet that is supposed to be protected. Could please let my know if there are any flaws in my code?
Trying to work with a protected workbook (i have access to the password) with a VBA project (1 UserForm, 5 Class Modules, 1 Standard Module)
I'm really confused about how to properly work with protected sheets in VBA.
I've tried doing an
ActiveSheet.Unprotect PWD
followed after a method call with
ActiveSheet.protect PWD
Also tried
ActiveSheet.protect PWD, UserInterfaceOnly:=True
from the Workbook_open sub
The issues is at random points its like the protection reverts to being protected and the parts of my VBA project that are making changes to the actual spreadsheet freak out.
If i pause the debugger go over and manually unprotect the sheet and hit continue things go off without a hitch
So what's going on here
Is it not enough to unprotect the sheet at the beginning of a method that calls other methods that make changes to the sheet and reportected it at the end?
or do I literally have to prefix and postfix
ActiveSheet.Unprotect PWD
ActiveSheet.Protect PWD
around EVERY single line that changes the sheet data?
Because it seems like the protection cares nothing about anything my VBA project does internally ONLY when I try to do subsequent things with the worksheet
I can post code later today when I get home if its needed
Is it not enough to unprotect the sheet at the beginning of a method that calls other methods that make changes to the sheet and reportected it at the end?
This is why global state is such a pain in the neck. As far as your VBA project is concerned, sheet protection state is global. So if you do:
Public Sub Procedure1()
Sheet1.Unprotect PWD
Procedure2
'do stuff on Sheet1
Sheet1.Protect PWD
End Sub
If Procedure2 re-protects Sheet1 before it exits, then Sheet1 is protected again when Procedure1 resumes to do stuff on Sheet1, and as you're experiencing, it doesn't like it much.
So yes, you need to be sure that the sheet isn't protected before you try to modify it.
or do I literally have to prefix and postfix [...] around EVERY single line that changes the sheet data?
You don't. I mean, you do, but you don't, if you manage your global state in a sane way. If anyone anywhere at any time can go in and re-protect the sheet you just unprotected, your swear jar can fill up rather quickly.
Similarly, Application.Calculation is global; yet if anyone anywhere can set it back to xlCalculationAutomatic after you carefully made it xlCalculationManual in an effort to "improve performance" (same goes for Application.ScreenUpdating), then you'll ultimately find yourself triggering recalculations and introduce very noticeable lags instead.
Global state is nice (hey I can access it from anywhere!), but also double-edged. If you don't structure things properly, global state quickly spagghettifies and becomes a tangled mess of inefficient back-and-forth toggles that you don't need, or want.
The solution is to set yourself up for success, and layer your code properly. Have all the sheet-protection-toggling code in one place, and constrain yourself to only ever invoke that logic from one single layer - everything underneath isn't concerned with sheet protection, it's none of its business. If the sheet it means to work with is protected, then it's not its problem - code in the upper layer has a bug.
In the above example, Procedure2 would be layer 2, and it shouldn't be allowed to toggle sheet protection at all. Let Procedure1 be responsible for that, and move the do stuff on Sheet1 part to some new Procedure3, that 's just as carelessly assuming sheet protection is already handled.
You can even encapsulate the toggling in a class, e.g. Sheet1Protection:
Option Explicit
Private Sub Class_Initialize()
Sheet1.Unprotect PWD
End Sub
Private Sub Class_Terminate()
Sheet1.Protect PWD
End Sub
And now you can do this:
Public Sub Procedure1
With New Sheet1Protection 'unprotects sheet1
Procedure2
Procedure3
End With ' object terminates, sheet1 is protected again
End Sub
Notice that sheet protection is being toggled at the very last minute, when it's needed, and wraps all operations that need to run with the unprotected sheet.
With tooling such as Rubberduck (an OSS add-in project for the VBE, which I manage & contribute to) you can easily locate all the places where sheet protection is being toggled outside of Sheet1Protection, and remove them.
So I am currently studying SQL Server but right now I am just working a standard office admin job while I'm studying.
I never really made macro's before and little knowledge on VB but decided to design a macro for work to make things a bit easier for my team.
The macro just very simply allows the user to enter data, stats etc and gives the percentage or average statistic resulting in a total letting the user know if the statistics have been hit that day, week, month etc.
It works well but I would like to add a "SUBMIT" button that when a user clicked it would send the data they have entered in specified cells to myself. I am not sure how to go about it, If needed I don't have access to systems like SQL, Visual Studio etc in work as said just basic admin job at the moment.
Would It need to be submitted as a CSV? or could it be submitted from the user's sheet straight onto another macro I have designed giving the results for the whole team? As said I am totally new to this idea.
Cheers Guys.
Awright, according to what you may need in a very simple approach, the first thing you need to do it's to know the cells where they're going to enter info (care with ranges ), let's assume for this example that whe only had one data entered in the first cell of the team worksheet. So, create a button called 'button1' or as you wish and on the click event use this code :
Private Sub button1_click()
Teamsheet.Cells(row,column) = Yoursheet.Cells(destinyrow,destinycolumn)
End Sub
That would copy the value from one sheet to another, now, if you had you sheet locked via password, you must unlock it before doing that,then lock it again so code would be like this :
Private Sub button1_click()
On Error Resume Next
yoursheet.unprotect password:="yourpassword"
Teamsheet.Cells(row,column) = Yoursheet.Cells(destinyrow,destinycolumn)
On Error Resume Next
yoursheet.PROTECT password:="yourpassword"
End Sub
I clarify that this is a very simple approach, so, if you're using specific cells you can copy one by one and this would do (so you can make anny calculation son your admin sheet), but when you're copying ranges should be like this :
Teamsheet.Range("A1:D3").Value = yoursheet.Range("A1:D3").Value
Also, always consider how they enter this data you need.
UPDATE :
Let's say you have a team workbook and yours is admin_workbook, concept it's similar. This code will do what you need but both workbooks should be at the same folder or path :
Private Sub button1_click()
Var_data = Teamsheet.Cells(row,column)
Application.ScreenUpdating = False
Workbooks.Open Filename:=ThisWorkbook.Path & "\admin_workbook.xls"
ThisWorkbook.Activate
Admin_sheet.Cells(destinyrow,destinycolumn) = var_data
Workbooks("admin_workbook.xls").Close SaveChanges:=True
Application.ScreenUpdating = True
End Sub
First you capture data on a var, then you open your admin book, put the data on the cell you want and close that workbook saving changes (you decide if you keep this line or mantain the workbook open and save manually). Also, Application.screenupdating it's a line that helps your screen doesn't flick when changing between workbooks.
Hope it helps friend !