Prevent Workbook Save BUT Save in Macro [duplicate] - vba

This question already has answers here:
Disable Excel save option but allow macro save
(2 answers)
Closed 5 years ago.
I am writing a code that will prevent the user from saving the workbook, and it will only save when I want it to. This is to prevent the user from making changes and saving when they are not supposed to. I have created two private subs, but I don't know how to make an exception when the workbook is being saved on my own. I would like to be able to place the saving code in various macros so that I can control the save at any point.
The following is my code:
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
MsgBox "You can't save this workbook!"
Cancel = True
End Sub
Private Sub Workbook_Open()
Dim myValue As String
Dim Answer As String
Dim MyNote As String
MsgBox "Welcome to the Lot Input Program"
If Range("A1").Value = "" Then
Line:
myValue = InputBox("Please input your email address:", "Input", "x#us.tel.com")
'Place your text here
MyNote = "Is this correct?: " & myValue
'Display MessageBox
Answer = MsgBox(MyNote, vbQuestion + vbYesNo, "Confirmation")
If Answer = vbNo Then
'Code for No button Press
GoTo Line
Else
Range("A1").Value = myValue
End If
ActiveWorkbook.Save
End If
End Sub

You may try something like this...
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
If Environ("UserName") <> "YourUserNameHere" Then
MsgBox "You can't save this workbook!"
Cancel = True
End If
End Sub
Edit:
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Dim Ans As VbMsgBoxResult
Ans = MsgBox("You can't save this workbook!" & vbNewLine & _
"Do you have password to save the file?", vbQuestion + vbYesNo)
If Ans = vbYes Then
frmPassword.Show 'UserForm to accept the password
Else
Cancel = True
End If
End Sub

I added a public variable saveLock that I reference in the save cancel code. This allows me to lock and unlock the save inside of my code. If anyone has a better way please let me know, but this did solve the problem.
Public saveLock As Integer
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
If saveLock = 0 Then
Cancel = True
End If
End Sub
Private Sub Workbook_Open()
Dim myValue As String
Dim Answer As String
Dim MyNote As String
saveLock = 0
MsgBox "Welcome to the Lot Input Program"
If Range("A1").Value = "" Then
Line:
myValue = InputBox("Please input your email address:", "Input", "x#us.tel.com")
'Place your text here
MyNote = "Is this correct?: " & myValue
'Display MessageBox
Answer = MsgBox(MyNote, vbQuestion + vbYesNo, "Confirmation")
If Answer = vbNo Then
'Code for No button Press
GoTo Line
Else
Range("A1").Value = myValue
End If
saveLock = 1
ActiveWorkbook.Save
saveLock = 0
End If
End Sub

Related

How to set a VSTO variable with VBA code?

I want to prevent the user from saving the VSTO project.
Public Class ThisWorkbook
Private Sub ThisApplication_WorkbookBeforeSave(Wb As Workbook, SaveAsUI As Boolean, ByRef Cancel As Boolean) Handles ThisApplication.WorkbookBeforeSave
Cancel = True
End Sub
end Class
My goal is to set one variable like VbaSave (as Boolean) and with one sub in VBA assign True or False at this variable then save or not the project.
The new code in my head will be:
in VSTO
Public Class ThisWorkbook
public VbaSave as Boolean = false
Private Sub ThisApplication_WorkbookBeforeSave(Wb As Workbook, SaveAsUI As Boolean, ByRef Cancel As Boolean) Handles ThisApplication.WorkbookBeforeSave
if VbaSave= false then Cancel = True
End Sub
end Class
In a VBA module
sub mysave()
myPath = Application.GetSaveAsFilename(FileFilter:= _
"Excel Files (*.xlsx), *.xlsx", title:="Save PO", _
InitialFileName:=ThisWorkbook.Path)
If myPath = "Falso" Then
msgStr = "ATTENTION !!!" & vbCrLf & "operazione annullata," & vbCrLf & _
"nessun file verrĂ  salvato."
MsgBox msgStr, vbInformation, "Revi4Utility.Info"
GoTo done
Else
VbaSave=true
ActiveWorkbook.SaveCopyAs fileName:=myPath
VbaSave=false
End If
end sub
This example crashes when I run mysave. The debug told me that VbaSave is not defined.
I created a public Boolean variable on VBA, and one function to retrieve the variable value.
In VSTO with .Application.Run I call the VBA function and I get the value.
VBA code:
' I define boolean value
Public SysSave As Boolean 'and I assign False like a STD value on my Ribbon load
'one function to get SysSave value
Function CanSave() As Boolean
CanSave = SysSave
End Function
' the sub to save the file
sub mysave()
myPath = Application.GetSaveAsFilename(FileFilter:= _
"Excel Files (*.xlsx), *.xlsx", title:="Save PO", _
InitialFileName:=ThisWorkbook.Path)
If myPath = "Falso" Then
msgStr = "ATTENTION !!!" & vbCrLf & "operazione annullata," & vbCrLf & "nessun file verrĂ  salvato."
MsgBox msgStr, vbInformation, "Revi4Utility.Info"
GoTo done
Else
SysSave=true
ActiveWorkbook.SaveCopyAs fileName:=myPath
SysSave=false
End If
end sub
VSTO code:
'Then I change the VSTO code
Public Class ThisWorkbook
Public SysSave As Boolean = False
Private Sub ThisApplication_WorkbookBeforeSave(Wb As Workbook, SaveAsUI As Boolean, ByRef Cancel As Boolean) Handles ThisApplication.WorkbookBeforeSave
SysSave = Globals.ThisWorkbook.Application.Run("CanSave")
If SysSave = False Then Cancel = True
End Sub
End Class

VBA 2 IF conditions applied for column

this is my first post, please be patient if I'm doing/asking something wrong.
My issue is:
I got 2 columns, A is number of children, B is name of those children.
Those values are manually entered, I simply would like to have B mandatory if A is filled.
Here is what I thought:
Private Sub Workbook_BeforeClose(Cancel As Boolean)
If Not IsEmpty(Sheet1.Range("A1")) Then
If IsEmpty(Sheet1.Range("B1")) Then
MsgBox "Please fill in cell B1 before closing."
Cancel = True
Else '
End If
End If
End Sub
This is actually working perfectly, unfortunately I can't manage to extend it for whole columns, when replacing A1 with A1:A1000 and B1 with B1:B1000 for instance,it doesn't work.
How can I validate this for both entire column A and B?
thanks in advance!
Try this
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Cancel = Evaluate("SUMPRODUCT(--(ISBLANK(Sheet1!B:B) <> ISBLANK(Sheet1!A:A)))")
If Cancel Then MsgBox "Please fill in column B before closing."
End Sub
EDIT
In order to take the user to the place where data is missing, and taking into account the additional information you provided about your data, try this:
'Private Sub Workbook_BeforeClose(Cancel As Boolean)
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Dim r: r = Evaluate( _
"MATCH(FALSE, ISBLANK('ELENCO AGGIORNATO'!V:V) = ISBLANK('ELENCO AGGIORNATO'!W:W), 0)")
If IsError(r) Then Exit Sub ' All is fine
Cancel = True
Application.Goto Sheets("ELENCO AGGIORNATO").Cells(r, "V").Resize(, 2)
msgBox "Please fill missing data before saving."
End Sub
Also note that I recommend Workbook_BeforeSave instead of Workbook_BeforeClose, because there's no harm if the user decides to drop his (incomplete) work and close the workbook without saving.
You may try something like this...
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Dim str As String
Dim Rng As Range, Cell As Range
Dim FoundBlank As Boolean
Set Rng = Sheet1.Range("A1:A1000")
str = "Please fill the cells listed below before colsing..." & vbNewLine & vbNewLine
For Each Cell In Rng
If Cell <> "" And Cell.Offset(0, 1) = "" Then
FoundBlank = True
str = str & Cell.Address(0, 1) & vbNewLine
End If
Next Cell
If FoundBlank Then
Cancel = True
MsgBox str, vbExclamation, "List of Blank Cells Found!"
End If
End Sub

Passing an user form result to vba code variable

I have a code that counts the files in a folder if they contain a specific string on their name.
For example: If I want it to count the files with close on their name (Close_26_03_2003.csv).
Currently the code reads the value of a cell in the sheet and searches for that string in the file name with the (InStr function). Problem is I have to write the type of file in the cell.
What I am trying to do is create an user form, with three option buttons (open, close and cancel). For open it sets the string equal to open, and search for files that have it on their name (same as for close). Cancel ends the sub.
Problem is I don't know which code I have to use in the user form for this and don't know how to pass it to the code that counts files (I though about assigning it to a variable).
Code as is:
Sub CountFiles3()
Dim path As String, count As Integer, i As Long, var As Integer
Dim ws As Worksheet
Dim Filename As String
Dim FileTypeUserForm As UserForm1
Application.Calculation = xlCalculationManual
path = ThisWorkbook.path & "\*.*"
Filename = Dir(path)
'the problem is here:
'x = user form result***************
'if cancel = true, end sub
Set ws = ThisWorkbook.Sheets("FILES")
i = 0
Do While Filename <> ""
'var = InStr(Filename, ws.Cells(2, 7).Value) 'this is current code, it checks if the cell has open or close
var = InStr(Filename, x)
If var <> 0 Then
i = i + 1
ws.Cells(i + 1, 1) = Filename
Filename = Dir()
Else: Filename = Dir()
End If
Loop
Application.Calculation = xlCalculationAutomatic
ws.Cells(1, 2) = i
MsgBox i & " : files found in folder"
End Sub
And this is my current user form code:
Private Sub Cancel_Click()
Me.Tag = 3 ' EndProcess
Me.Hide
End Sub
Private Sub ClosingType_Click()
Me.Tag = 2 ' "CLOSING"
Me.Hide
End Sub
Private Sub OpeningType_Click()
Me.Tag = 1 ' "OPENING"
Me.Hide
End Sub
Any ideas?
add following code to your CountFiles3() sub in the "'the problem is here:" section:
Dim x As String
x = GetValue
If x = "end" Then Exit Sub
then add following code in any module:
Function GetValue()
With MyUserForm '<--| change "MyUserForm " to your actual UserForm name
.Show
GetValue = .Tag
End With
Unload MyUserForm '<--| change "MyUserForm " to your actual UserForm name
End Function
and change your Userform code as follwos
Private Sub Cancel_Click()
Me.Tag = "end" ' EndProcess
Me.Hide
End Sub
Private Sub ClosingType_Click()
Me.Tag = "close" ' "CLOSING"
Me.Hide
End Sub
Private Sub OpeningType_Click()
Me.Tag = "open" ' "OPENING"
Me.Hide
End Sub

Setting validation for combo box error

#
updated codes
Function condition(ByRef objCmb As ComboBox)
If objCmb.Value ="" And objCmb.Value = "g" Then
Call MsgBox("gg", vbOKOnly, "error")
End If
End Function
Private Sub ComboBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean)
condition (ComboBox1)
End Sub
'other codes for reference:
Private Sub CommandButton1_Click()
Dim lastrow As Integer
lastrow = Cells(Rows.Count, "A").End(xlUp).Row
For i = 1 To 3
For j = 1 To 5
With Me.Controls("ComboBox" & (i - 1) * 5 + j)
If .Text <> "" Then
Cells(lastrow + i, j) = .Text
Else
Exit Sub
End If
End With
Next j
Next i
End Sub
I have 50 combo and text boxes in VBA user panel. As it is too troublesome to set constraints in every combo or text box, I want a function to apply to every combo and text box.
For the codes above , it shows up cant find objecterror
How to solve ?
Btw , how to set the function statement for textbox ?
is it Function condition2(ByRef objCmb As textbox)...
You are receiving an error because ComboBox is not ByRef objCmb As ComboBox. Don't use parenthesis when calling a sub. Don't use parenthesis when calling function if you are not using the functions return value. If a function does not return a value it should be a sub.
Sub condition(ByRef objCmb As MSForms.ComboBox)
If objCmb.Value <> "" And objCmb.Value = "g" Then
MsgBox "gg", vbOKOnly, "error"
objCmb.Value = ""
End If
End Sub
Private Sub ComboBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean)
condition ComboBox1
End Sub
I wrote a function to help you generate the Exit event code for all your text and combo boxes.
Sub AddCodeToCipBoard(frm As UserForm)
Const BaseCode = " Private Sub #Ctrl_Exit(ByVal Cancel As MSForms.ReturnBoolean)" & vbCrLf & _
" condition ComboBox1" & vbCrLf & _
" End Sub" & vbCrLf & vbCrLf
Dim s As String
Dim ctrl
Dim clip As DataObject
Set clip = New DataObject
For Each ctrl In frm.Controls
If TypeName(ctrl) = "ComboBox" Or TypeName(ctrl) = "TextBox" Then
s = s & Replace(BaseCode, "#Ctrl", ctrl.Name)
End If
Next
clip.SetText s
clip.PutInClipboard
End Sub
Put this code in a module and call it like this:
AddCodeToCipBoard Userform1
Now all the Exit event code will be copied into the Windows Clipboard. Go into your Userforms code module and paste the new code.
Example Output:
Private Sub TextBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean)
condition ComboBox1
End Sub
Private Sub TextBox2_Exit(ByVal Cancel As MSForms.ReturnBoolean)
condition ComboBox1
End Sub
Private Sub TextBox3_Exit(ByVal Cancel As MSForms.ReturnBoolean)
condition ComboBox1
End Sub
Private Sub ComboBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean)
condition ComboBox1
End Sub
Private Sub TextBox4_Exit(ByVal Cancel As MSForms.ReturnBoolean)
condition ComboBox1
End Sub

Runtime Error 1004 while trying to add a range to a script in VBA

The script below works fine for a single cell, but when I am trying to add the Range Q3:Q200, it throws the Runtime Error 1004.
VBA script:
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As _
Boolean, Cancel As Boolean)
With Sheets(" Sheet3 ").Range(" Q3 ")
If .Value = " Shirts " Then
If Range(" R3 ") = "" Then
MsgBox " Save has been cancelled. You must fill Type of Shirts "
Cancel = True
End If
End If
End With
End Sub
You need to iterate through the range to find values of "Shirts". Try this:
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Dim c As Range
For Each c In Range("Sheet3!Q3:Q200")
Debug.Print c.Address & " " & c.Value
If c.Value = "Shirts" Then
If c.Offset(0, 1).Value = "" Then
MsgBox "Save has been cancelled. You must fill Type of Shirts"
Cancel = True
Exit Sub
End If
End If
Next
End Sub
Alternatively, you could use Range("Sheets3!Q3:Q200").Find("Shirts") but I think that will actually require more code.