What it does: Requires fields from users. Blocks user from saving if specific fields are missing. Turns those fields red until saved correctly.
What I need: Well, how the hell am I supposed to save this...
What I would like: Since the worksheet is blank. I cannot save. and required fields are red. EVEN if I could save I would LIKE the cells to be on no fill until I roll it out.
View Original Post Here
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Dim xlSht As Worksheet
Dim cellsNotPopulated As Boolean
cellsNotPopulated = False
Set xlSht = ThisWorkbook.Worksheets("1st Call")
With xlSht
If .Range("F7") = "" Then
.Range("F7").Interior.Color = RGB(255, 0, 0)
cellsNotPopulated = True
Else
.Range("F7").Interior.ColorIndex = xlNone
End If
End With
If cellsNotPopulated = True Then
MsgBox "Please review the highlighted cells and ensure the fields are populated."
Cancel = True
End If
End Sub
If you are in the middle of development and want to "break the rules" and save your current efforts, then in a standard module:
Sub MyPrivateSave()
Application.EnableEvents = False
ThisWorkbook.Save
Application.EnableEvents = True
End Sub
Of course, when you finish development, you would remove this little "save tool" before you send the workbook out to the users.
or add as the first line if environ("Username")=your username then exit sub
Related
I'd like to imitate the behavior of the default insert comment button with a macro. I want to store all of my macros in the Personal workbook, not the active workbook.
I'd like it to simply create a comment and then set the focus to that empty comment.
Below is what I have so far, using Terry's suggestion to make the comment .Visible and then .Shape.Select it:
Sub addNewComment()
Dim authorName As String
Dim authorNameLength As Integer
authorName = Application.UserName
authorNameLength = Len(authorName)
ActiveCell.AddComment _
authorName & ":" _
& Chr(10)
With ActiveCell.Comment
With .Shape
.AutoShapeType = msoShapeFoldedCorner
.Fill.ForeColor.RGB = RGB(215, 224, 239)
With .TextFrame
.AutoSize = True
.Characters.Font.Size = 11
.Characters.Font.Name = "Calibri"
.Characters(1, (authorNameLength + 1)).Font.Bold = True
.Characters((authorNameLength + 2), 1).Font.Bold = False
End With
End With
.Visible = True
.Shape.Select True
End With
End Sub
I'm not sure how to get the comment to go back to not being visible. Do I store the reference to the cell I just added the comment to, and then refer to that cell with the Worksheet_SelectionChange event? Or do I make that event just hide all comments on the sheet? Is it possible to use Worksheet_SelectionChange at all with the Personal workbook?
Also, my comment box does not resize as I type and add line breaks. It does resize after I exit, but actually too large by about four lines. Not sure why that is happening.
I'm sure there is a cleaner way to organize my With blocks as well.
I tried using the following to hide the comment again after selecting another cell:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Target.Comment.Visible = False
End Sub
I received the following error:
error 91: Object variable or With block variable not set
You can select the comment once you make it visible using the following:
With range("a1")
.Comment.Visible = True
.Comment.Shape.Select True
End With
But I think you'll need to have another macro to hide the comment again once you deselect, as otherwise it will stay visible. You could try doing this on the SelectionChange event of the worksheet:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Target.Comment.Visible = False
End Sub
I have a file that I made with three levels of permissions.
You get prompted to input a password on a userform and you get either admin rights, read/write or only read permissions.
That works fine, unless you don't enable macros.
If you don't enable the macro prompting the userform is not shown and the rights are not set thus you have full access.
I have seen you can use a splash screen but for that to work you need to save the workbook as far as I understand?
The basic concept of them is to beforeclose hide sheets and unhide splash screen, then save the workbook.
But what if the user made a mistake and what to close without save and reopen later? The file gets closed with all sheets visible?
So I thought maybe I can use the "explorer -> right click -> properties -> Read-only" attribute as an extra layer of protection?
I found these functions.
ActiveWorkbook.ChangeFileAccess Mode:=xlreadonly
ActiveWorkbook.ChangeFileAccess Mode:=xlreadwrite
But I tried the readonly line and it did set read only, file could not be saved.
Then I closed the workbook without saving and opened again. But it seems the attribute was not set in the properties of the file since it was not tickmarked and when I opened the file I could save it.
Is there any other way to solve this?
I want to either "force" VBA on the user or make sure he/she can't save the file if it's opened without VBA.
I found a solution that seems to work.
You can in Workbook_BeforeClose use this line to make the file read-only in file properties.
SetAttr Application.ActiveWorkbook.FullName, vbReadonly
This will set the tickmark in the properties and Excel will notice the file is writeprotected.
Then in Workbook_Open() (or as in my case when rights has been established)
SetAttr Application.ActiveWorkbook.FullName, vbReadwrite
ActiveWorkbook.ChangeFileAccess Mode:=xlReadWrite
The first line removes the tickmark in file properties, but Excel still "remembers" the file as read-only.
The second line will tell Excel to make it ReadWrite and the file works as normal again.
This is a far more complicated method than Adreas', but doesn't feature the same risk of mildy tech-savvy users just right-clicking the file and popping into the "Properties" panel.
Create 2 new worksheets. One of them is set to xlVeryHidden - for the examples below, I have called it hsSheetStatus. The other, with a nice large notice telling your hapless minion colleague to enable Macros, will be changing visibility. I have called this hsEnableNotice.
Then I have a Macro to show hsEnableNotice hide all of the other sheets (storing their visibility and the ActiveSheet on hsSheetStatus), and a second Macro to do the reverse (restore visibility/ActiveSheet from hsSheetStatus) and set these to run on Workbook_BeforeSave, Workbook_AfterSave and Workbook_Open:
Option Explicit
Private Sub Workbook_AfterSave(ByVal Success As Boolean)
UnlockAndShow
End Sub
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
LockAndHide
End Sub
Private Sub Workbook_Open()
UnlockAndShow
End Sub
Private Sub LockAndHide()
Dim lSheet As Long, ActiveName As String
Application.ScreenUpdating = False
Application.EnableEvents = False
ActiveName = ThisWorkbook.ActiveSheet.Name
hsEnableNotice.Visible = xlSheetVisible
hsEnableNotice.Activate
hsSheetStatus.Range(hsSheetStatus.Cells(1, 1), hsSheetStatus.Cells(hsSheetStatus.Rows.Count, 1)).EntireRow.Delete
For lSheet = 1 To ThisWorkbook.Sheets.Count 'By using Sheets instead of Worksheets, we include Charts etc
hsSheetStatus.Cells(lSheet, 1).Value = ThisWorkbook.Sheets(lSheet).Name
hsSheetStatus.Cells(lSheet, 2).Value = ThisWorkbook.Sheets(lSheet).Visible
If ThisWorkbook.Sheets(lSheet).Name = ActiveName Then hsSheetStatus.Cells(lSheet, 3).Value = 1
If ThisWorkbook.Sheets(lSheet).Name <> hsEnableNotice.Name Then ThisWorkbook.Sheets(lSheet).Visible = xlSheetVeryHidden
Next lSheet
ThisWorkbook.Protect Password:="ThisIsMyPassword.ThereAreManyLikeIt,ButThisOneIsMine.", Structure:=True, Windows:=False
Application.EnableEvents = True
End Sub
Private Sub UnlockAndShow()
Dim WasSaved As Boolean, lSheet As Long, lMax As Long
WasSaved = ThisWorkbook.Saved
Application.ScreenUpdating = False
Application.EnableEvents = False
lMax = hsSheetStatus.Cells(hsSheetStatus.Rows.Count, 1).End(xlUp).Row
ThisWorkbook.Unprotect Password:="ThisIsMyPassword.ThereAreManyLikeIt,ButThisOneIsMine."
For lSheet = 1 To lMax
ThisWorkbook.Sheets(hsSheetStatus.Cells(lSheet, 1).Value).Visible = hsSheetStatus.Cells(lSheet, 2).Value
If hsSheetStatus.Cells(lSheet, 3).Value = 1 Then ThisWorkbook.Sheets(hsSheetStatus.Cells(lSheet, 1).Value).Activate
Next lSheet
hsSheetStatus.Visible = xlSheetVeryHidden
hsEnableNotice.Visible = xlSheetVeryHidden
Application.EnableEvents = True
Application.ScreenUpdating = True
ThisWorkbook.Saved = WasSaved
End Sub
The code below ran earlier but will not execute when a cell is double clicked.
Private Sub Worksheet_DoubleClick(ByVal Target As range, Cancel As Boolean)
If Target.Font.Bold = False Then
Target.Font.Bold = True
Target.Font.Color = vbRed
Else
Target.Font.Bold = False
Target.Font.Color = 1
End If
End Sub
Not an answer to why it's not working (#Mat's Mug and #Scott Craner beat me to that again), but a shortened version of the code.
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
With Target.Font
.Bold = Not .Bold
.Color = Choose(Abs(CLng(.Bold)) + 1, 1, vbRed)
End With
End Sub
Ok, not as easy to follow as the original but here's what it's doing:
Target.Font.Bold is either TRUE or FALSE, so Not .Bold will return the opposite.
Bold = TRUE so Not Bold = FALSE
Abs(CLng(.Bold)) + 1
Again, .Bold is either TRUE or FALSE. Numerically TRUE = -1, FALSE = 0.
CLNG(.Bold) will return -1 or 0.
ABS(CLNG(.Bold)) will return 1 or 0.
Abs(CLng(.Bold)) + 1 will return 1 or 2 - which is used in the CHOOSE command to return vbRed or 1.
DO NOT type any of these signatures manually!
Use the code pane dropdowns instead:
Select Worksheet from the left dropdown, and pick an event to handle in the right dropdown; the VBE will generate a method stub with the proper signature for you.
Typing them out manually off the top of your head can (and does!) result with handlers that end up never being called, or worse, that are called, but are given parameter values in the wrong arguments, e.g. if UserForm_QueryClose is typed up manually with inverted parameters (the handler has 2 Integer parameters, so you need to remember the exact order.. otherwise you assign Cancel and the form understands that you assigned CloseMode)
If you're not seeing Worksheet in the left dropdown, then you're not in a worksheet's code-behind module. Worksheet events can only be handled in a worksheet module.
In a Workbook module (i.e. ThisWorkbook) you can handle the SheetBeforeDoubleClick event to handle a double-click on any worksheet in the workbook:
Private Sub Workbook_SheetBeforeDoubleClick(ByVal Sh As Object, ByVal Target As Range, Cancel As Boolean)
End Sub
So I have been trying to put together a little Excel sheet, that has an entry Log in it. So whenever the sheet is closed, Name, Date and Time are added.
So basically I have three macro running, I will only mention two. The main macro will ask if I want to close the sheet and I will have to answer with yes or no. This works fine. If I press yes the main macro will call a sub macro, that will ask me to enter a string. If this Inputbox is empty or the entry is canceled, I want the main sub to stop running and cancel the Close process. Which won't seem to work. The error in the code to me seems pretty clear, but I don't know how to prevent it and find a better solution. If you could help me come up with a solution I would really appreciate it.
This line of code seems to be the problem:
If Cancel_Button_LOG = False Then Cancel = True
Here I will add compressed versions of the two macros
Public Sub Add_Entry_to_Log()
Dim i As Integer
Dim response As Variant
Cancel_Button_LOG = True
response = InputBox("Please enter your Name", "Name")
If response <> "" Then
Else
Cancel_Button_LOG = False
MsgBox "Please enter your name", vbExclamation + vbOKOnly, "Name"
End If
Worksheets("Log").Protect "secret"
ThisWorkbook.Save
End Sub
Now I will want to use the Cancel_Button_log Variable to cancel the main sub:
Dim answer As Variant
answer = MsgBox("Are your sure you want to close the workbook?", vbYesNo) Cancel = False
Select Case answer
Case Is = vbYes
Worksheets("Log").Unprotect "secret"
Call Test
Call Add_Entry_to_Log
If Cancel_Button_LOG = False Then Cancel = True
Worksheets("Log").Protect "secret"
Case Is = vbNo
Cancel = True
End Select
ThisWorkbook.Save
End Sub
I think you're doing this in the most complicated way possible. If I understand your requirements correctly - you can replace all your code in your ThisWorkbook module with something like this:
Const WB_LOG As String = "Log" '// name of sheet that the log is in
Private Sub Workbook_BeforeClose(Cancel As Boolean)
If MsgBox("Do you really want to close the workbook?", vbYesNo) = vbYes Then
With Sheets(WB_LOG)
.Range("A" & .Rows.Count).End(xlUp).Offset(1, 0).Resize(1, 2).Value = Array(Environ$("USERNAME"), Now)
End With
ThisWorkbook.Save
Else
Cancel = True
End If
End Sub
Private Sub Workbook_Open()
With Sheets(WB_LOG)
.Protect Password:="secret", UserInterfaceOnly:=True
.Range("A1:B1").Value = Array("USERNAME", "TIMESTAMP")
End With
End Sub
This would negate the need for the user to manually insert their name (assuming their system username would suffice*) and also negate the need to unprotect the worksheet each time as I've used the UserInterfaceOnly option.
* Environment variables such as %USERNAME% can be falsified if a user wishes to do so and knows how - however someone typing their name into a textbox is even easier to falsify...
I have a combobox in my worksheet with a dynamic search, the dynamic search is done by searching the typed letters in a list that is on another worksheet. The search is done by excel formulas. The list is a defined range and then the combobox listfillrange is set to the named range.
When I type the dropdown list opens, for certain items the combobox allows me to select from the list and for others the selection disappear as I select. I tried figure why do these items disappear. The list is long (10,000 items) so it kind of works slow, but I am not sure if this is the problem.
How can I fix such a problem? Is there a way to define a variable for the mouseclick selection from dropdown list?
Thanks in advance,
Tali
This is my code:
Private Sub ComboBox1_Change()
Sheets("PS").EnableCalculation = True
ComboBox1.ListFillRange = "DropDownList"
ComboBox1.DropDown
End Sub
Private Sub CommandButton21_Click()
Dim PS As Worksheet
Application.ScreenUpdating = False
Application.ErrorCheckingOptions.BackgroundChecking = False
Sheets("PharmaSoft").Select
Set PS = Sheets("PS")
SelectionA = PS.Range("J2").Value
If ComboBox1.Value = SelectionA Then
Range("J19") = "Pharmacy purchase price"
Range("N19") = PS.Range("K2")
Range("O19") = "ILS"
Range("J21") = "Pharmacy selling price Incl.VAT"
Range("N21") = PS.Range("L2")
Range("O21") = "ILS"
Range("J23") = "Package size"
Range("N23") = PS.Range("M2")
Range("J19:O23").Select
With Selection.Font
.ThemeColor = xlThemeColorDark1
.TintAndShade = 0
End With
Selection.Font.Bold = True
Application.ScreenUpdating = True
Range("N19").Errors.Item(1).Ignore = True
Range("N21").Errors.Item(1).Ignore = True
Range("N23").Errors.Item(1).Ignore = True
Else
MsgBox "Please select a product", vbCritical, "Error"
End If
Sheets("PS").EnableCalculation = False
End Sub
Private Sub CommandButton22_Click()
Application.ScreenUpdating = False
Sheets("PharmaSoft").Select
ComboBox1.Value = Null
Range("J19:O23").Value = Null
Application.ScreenUpdating = True
End Sub
Also the code for the workbook:
Private Sub Workbook_Open()
Sheets("PharmaSoft").Select
Application.ScreenUpdating = False
Sheets("PharmaSoft").ComboBox1.Value = Null
Range("J19:O23").Value = Null
Application.ScreenUpdating = True
End Sub
Although I can't comment on what you're doing given that your search is done as you mention with excel formulas. I do know that using data validation via a combo box can be pretty quick. The method I use is as per this page and is extremely fast. It's pretty much instantaneous on a validation range that is about 15k rows long. The best part about it is that it provides auto completion. So when you type in the combo box and that value isn't in the list, the entry that matched one character ago disappears. It's a good visual cue whether you're selection is valid or not. And of course, you can still use the drop down box in the usual way. The only down side is that, as coded at the link provided, you have to double-click to enter the auto-completion mode.