I did macro that creates a graph but need to make the file password protected.
Of course, when I protect the file, the macro will stop working.
I inserted the below into my code to unprotect the file, run the code, then protect the file again. As I the code is a function, I had to create two sub procedures, which is maybe the reason why the trick is not functioning.
Any idea how I can fix this?
Option Explicit
Sub protection()
Worksheets("Sheet1").Unprotect "abc123"
End Sub
Function (here is my function code)
End Function
Sub protection2()
Worksheets("Sheet1").protect "abc123"
End Sub
I guess you want to start one sub procedure to do the trick. My example unprotects your sheet, let the function do its magic and protects the sheet.
Option Explicit
Sub protection()
Worksheets("Sheet1").Unprotect "abc123"
Call Function (here may be values for your arguments)
Worksheets("Sheet1").protect "abc123"
End Sub
Function (here may be prameters)
the function code belongs here
End Function
On a protected sheet you can't changed cells which are locked. You may wan't to play around with the attached code:
Option Explicit
Sub SheetSetup()
Range("B3:C7").Locked = False
Range("E3:F7").Locked = True 'This is default
End Sub
Sub Sample_ProtectedSheet()
ClearValues
ChangeAllValues_on_ProtectedSheet
MsgBox ("Only values in ""B4:C7"" are set to ""yes""!")
End Sub
Sub Sample_UnprotectedSheet()
ClearValues
ChangeAllValues_on_UnprotectedSheet
MsgBox ("All values set to ""yes""!")
End Sub
Function ChangeAllValues_on_UnprotectedSheet()
Call Unprotect
On Error Resume Next
Range("B4:C7").Value = "yes"
Range("E4:F7").Value = "yes"
On Error GoTo 0
End Function
Function ClearValues()
Call Unprotect
On Error Resume Next
Range("B4:C7").Value = ""
Range("E4:F7").Value = ""
On Error GoTo 0
End Function
Function ChangeAllValues_on_ProtectedSheet()
Call Protect
On Error Resume Next
Range("B4:C7").Value = "yes"
Range("E4:C7").Value = "yes"
On Error GoTo 0
End Function
Sub Protect()
Worksheets("Sheet1").Protect "abc123"
End Sub
Sub Unprotect()
Worksheets("Sheet1").Unprotect "abc123"
End Sub
Related
I have a sub that calls another sub and most often I need to exit the called sub and stop executing the rest of the sub that called it.
So I have the sub Add_Format_Button_Click that calls another sub:
call CheckEmptyComboboxValues
And most often CheckEmptyComboboxValues will end up on
Exit sub
But the Add_Format_Button_Click will continue executing the rest of it. This is where I need help because when CheckEmptyComboboxValues exits, I need the Add_Format_Button_Click to also exit.
Could someone help me solve this, seemingly simple problem?
EDIT: CheckEmptyComboboxValues contains tens if not hundreds of if statements that follow the structure of:
If Format_Layout.FilmType.Value = "" Then
MsgBox "Specify a film type!", , ("Film Type"): Exit sub
End If
So I guess I need to turn that exit sub value into something I can check in the main sub with an if statement like
if CheckEmptyComboboxValues .value = "Exit sub" Then
exit sub
3 solutions, each of them works for your case. I would go with the first or last solution. Depends a bit on the design of the rest of your project:
1. Function
Function CheckEmptyComboboxValues() As Boolean
If AnythingOddHappens Then
Exit Function
End If
CheckEmptyComboboxValues = True
End Function
The result of this function is only True if it runs through the end, whenever you exit it before it is False
Sub Example()
If Not CheckEmptyComboboxValues Then Exit Sub
'instead of
'Call CheckEmptyComboboxValues
End Sub
2. Procedure with cancel
Sub CheckEmptyComboboxValues(ByRef Cancel As Boolean)
If AnythingOddHappens Then
Cancel = True
Exit Sub
End If
End Sub
Sub Example()
Dim Cancel As Boolean
Call CheckEmptyComboboxValues(Cancel)
If Cancel Then Exit Sub
End Sub
This can be used, but here I see no advantage and would prefer the function above. This method can be useful if you already have a function with a return value and want an additional cancel or in case you write code for an event.
3. Throw an error
Sub CheckEmptyComboboxValues()
If AnythingOddHappens Then
Err.Raise vbObjectError + 513, "CheckEmptyComboboxValues", "Something odd happened"
Exit Sub
End If
End Sub
Sub Example()
On Error Goto ERR_HANDLER
Call CheckEmptyComboboxValues
Exit Sub
ERR_HANDLER:
MsgBox "check failed"
End Sub
This can be an advantage if you have multiple check procedures/functions that can throw errors. Compared with the first or second solution where you need to check the result of each single check procedure/function this solution needs only one error handler, and threfore comes with slim code.
This also has the advantage that the check procedure now can return different type of errors that your calling sub can use to provide further assistance to the user. While the first 2 solutions are more like "It didn't pass the check" the last solution can provide more detailed information like "It didn't pass the check because of A/B/C happened" depending on which error was raised.
Sub CheckEmptyComboboxValues()
If AnythingOddHappens Then
Err.Raise vbObjectError + 513, "CheckEmptyComboboxValues", "Something odd happened"
Exit Sub
End If
' more code here …
If AnythingDifferentOddHappens Then
Err.Raise vbObjectError + 514, "CheckEmptyComboboxValues", "Something different odd happened"
Exit Sub
End If
End Sub
Now you can use the different error numbers to provide a more detailde infomation to the user about what went wrong and not just something went wrong.
Sub Example()
On Error Goto ERR_HANDLER
Call CheckEmptyComboboxValues
Exit Sub
ERR_HANDLER: 'possibility to give a more detailed information
Select Case Err.Number
Case vbObjectError + 513
MsgBox "check A failed"
Case vbObjectError + 514
MsgBox "check B failed"
Case Else
MsgBox "Undefined fail"
End Select
End Sub
Rewrite CheckEmptyComboboxValues to a function which returns a boolean. In Add_Format_Button_Click you can then write
if CheckEmptyComboboxValues then
' You could give a message
Exit Sub
else
' rest of the code of
' Add_Format_Button_Click
end if
CheckEmptyComboboxValues might look like that
Function CheckEmptyComboboxValues as boolean
' code goes here
if .... then
CheckEmptyComboboxValues = False
exit function
else
' other code
end if
CheckEmptyComboboxValues = True
End Function
Update based on the edit
Function CheckEmptyComboboxValues() As Boolean
' ....
If Format_Layout.FilmType.Value = "" Then
MsgBox "Specify a film type!", , ("Film Type")
CheckEmptyComboboxValues = True
Exit Function
End If
' ....
CheckEmptyComboboxValues = False
End Function
I'm trying to make a VB function that's going to add an image from the file path and add a hyperlink to it.
It needs to be called from a function, it can't be a Sub.
Here's the code I have so far:
Function AddHyperlinkedImage()
InsertPictureHyperlink
End Function
Sub InsertPictureHyperlink()
Dim pct As Picture, iLeft#, iTop#
Dim sFile As String
sFile = "C:\somepath\picture.jpg"
If Dir(sFile) = "" Then
Exit Sub
End If
With Range("A1")
.Select
iLeft = .Left: iTop = .Top
End With
Set pct = ActiveSheet.Pictures.Insert(sFile)
pct.Left = iLeft
pct.Top = iTop
With Worksheets("Sheet1")
.Hyperlinks.Add Anchor:=.Shapes(pct.Name), Address:="somexcel.xlsx"
End With
End Sub
It adds the picture, but it won't add the hyperlink. When I run the sub by itself, it adds the picture and hyperlinks it.
But I need it to be in the form of a function. It can't be a button or anything like that. I have to be able to call it with =SomeFunction()
For the life of me I can't figure out why it works when I just call it, but it doesn't work when I call the sub from inside a function.
Is there a way to do this?
So instead of a UDF you can try to utilize a Worksheet_Change event that will call your InsertPictureHyperlink() macro. To ensure the macro does not fire anytime you change a cell, add a condition to only fire the macro when a certain keyword is entered. Here the keyword will be AddHyperlinkedImage
Private Sub Worksheet_Change(ByVal Target As Range)
If Target = "AddHyperlinkedImage" Then
Application.EnableEvents = False
InsertPictureHyperlink
Application.EnableEvents = True
End If
End Sub
Sub InsertPictureHyperlink()
MsgBox "Macro enabled - add your code in this sub"
End Sub
This is just an example of what I want to do. I have an elaborate macro that I want to do different things depending on whether it was called by another macro or not.
sub Example()
Call MyCode
end sub
sub MyCode()
If Called by Example GoTo SkipNextLine
Do these things
exit sub
SkipNextLine:
Do other things
end sub
You can create hidden name (which, actually, isn't tied to range). Think of it as global variable. The difference between global variable and this name is that name is persisted in workbook when you close it. When you open workbook again - you can start using it without any initialization. As a bonus, this name won't be displayed in Name Manager. The defining of name is required only once.
Sub SetHiddenName()
Names.Add Name:="Caller", RefersTo:="StartValue", Visible:=False
End Sub
Sub FF()
Names("Caller").Value = "FF"
Call SS
End Sub
Sub SS()
Select Case [Caller]
Case "FF": MsgBox "Called by FF" '...
Case "ZZ": MsgBox "Called by ZZ"
End Select
End Sub
A simple approach would be to use arguments and parameters.
Sub Example()
Call MyCode("Example")
End Sub
Sub Example2()
Call MyCode("Example2")
End Sub
Sub MyCode(Origin as String)
Select Case Origin
Case "Example"
'Do stuff here
Case "Example2"
'Do other stuff here
End Select
End Sub
I made my way to this post wanting a macro that changed things on the sheet, but not wanting to kick off event driven macros. In case it's also useful for someone else, it's possible to turn these off in excel using Application.EnableEvents in the parent macro using:
Sub parentMacro()
Application.EnableEvents = False
'Do stuf here
Application.EnableEvents = True
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
'this now only is called on worksheet changes outside of parent macro,
'as it's disabled while parent runs
'Note: This disables all event macros running, so might not be perfect for all cases
End Sub
I've been searching and can't find what the cause of my error is.
I'm creating a VBA application in Excel to assist in processing financial reports. I have the basic functionality complete and am trying to build an Admin function to allow end users to log in and change the multiplier in the equation.
I have 2 userforms, frmLogin and frmAdmin.
frmLogin - is very basic, however when it tries to do frmAdmin.Show i get the run-time 424 error.
frmLogin
Private Sub LoginButton_Click()
If Me.txtUsername.Value = "Admin" Then
If Me.txtPassword.Value = "password" Then
Unload Me
frmAdmin.Show
Exit Sub
Else
MsgBox "Sorry, Incorrect Login Details"
Me.txtUsername.SetFocus
End If
Else
MsgBox "Sorry, Incorrect Login Details"
Me.txtUsername.SetFocus
End If
End Sub
frmAdmin
Private Sub Save_Click()
Workbook_Refresh
SaveBackup
SaveSettings
CloseForm
End Sub
Private Sub Update_Click()
civa.Value = Sheets("Settings").Range("b1")
oral.Value = Sheets("Settings").Range("b2")
End Sub
Private Sub UserForm_Initialize()
Settings.Range("b2") = civa.Value
Settings.Range("b3") = oral.Value
End Sub
Private Sub SaveSettings()
ActiveWorkbook.Save SaveChanges:=True
End Sub
Private Sub CloseForm()
Application.Goto Worksheets("Home Screen").Range("D4")
Unload Me
End Sub
WorkbookRefresh is called from Module1.
Any help will be greatly appreciated.
Steven
Settings.Range("b2") = civa.Value doesn't make sense in the UserForm_Initialize() event.
As civa is a control on the form, it doesn't have a value yet, so you can't use it to assign a value to a cell in your sheet.
Instead, I think you meant civa.Value = Settings.Range("b2")
I have added an activex combobox "ComboBox1" to my excel spreadsheet "Sheet1" and I want to be able to call different macros based on the selection in the combobox. Some of the macro names I have are "MC323", "MC616", "MC813".
So basically you select MC616 from the combobox list I want it to run the MC616 macro.
I have tried searching for answers but nothing has made since to me. Here is what I have so far, which isn't much and I'm sure isn't right anyway.
Option Explicit
Private Sub Workbook_Open()
With Sheet1.ComboBox1
ComboBox1.Clear
.AddItem "MC323"
.AddItem "MC616"
.AddItem "MC813"
End With
End Sub
Sub ComoBox1_Change()
With Sheet1.ComboBox1
Select Case ComboBox1.Value
Case "MC323": MC323
Case "MC616": MC616
Case "MC813": MC813
End Select
End With
End Sub
Sub MC323()
Call MC323
End Sub
Sub MC616()
Call MC616
End Sub
Sub MC813()
Call MC813
End Sub
You have a spelling error, that could be an issue, anyway...
This should go into the Sheet1 module
Sub ComboBox1_Click()
Select Case ComboBox1.Value
Case Is = "MC323": MC323
Case Is = "MC616": MC616
Case Is = "MC813": MC813
End Select
End Sub
This should go into a regular module,
Sub MC323()
MsgBox "MC323"
End Sub
Sub MC616()
MsgBox "MC616"
End Sub
Sub MC813()
MsgBox "MC813"
End Sub
This is in the Workbook Module,
Private Sub Workbook_Open()
With Sheet1.ComboBox1
.Clear
.AddItem "MC323"
.AddItem "MC616"
.AddItem "MC813"
End With
End Sub
It all looks good until you get here:
Sub MC323()
Call MC323
End Sub
Sub MC616()
Call MC616
End Sub
Sub MC813()
Call MC813
End Sub
Subroutine MC323 has only one line and that line says to call subroutine MC323 which has only one line and that line and that line says to call subroutine MC323 which has only one line and that line says to call subroutine MC323 which has only one line and that line says to call subroutine MC323 which has only one line and that line says to call subroutine MC323 which has only one line and that line says to call subroutine MC323 which has only one line and that line asays to call subroutine MC323.
So anyway, you get into a endless recursive loop, which is no good. Don't call subroutine MC323 inside of MC323. Put the code you want ran into the subroutine MC323. Like...
Sub MC323()
MsgBox("Endless recursive loops are bad")
End Sub
I've been wondering why I couldn't get my examples to work - MC323, MC616 and MC813 are also worksheet cell references.
So..... ensure you have a sheet with a codename Sheet1 and an ActiveX combo-box called ComboBox1.
In the ThisWorkbook module:
Private Sub Workbook_Open()
With Sheet1
.ComboBox1.Clear
.ComboBox1.AddItem "MC323"
.ComboBox1.AddItem "MyMacro2"
.ComboBox1.AddItem "MyMacro3"
End With
End Sub
Note the first item will cause an error - 'Cannot run the macro 'MC323'. The macro may not be available in this workbook or all macros may be disabled.
In a normal module:
Public Sub MC323()
MsgBox "1"
End Sub
Public Sub MyMacro2()
MsgBox "2"
End Sub
Public Sub MyMacro3()
MsgBox "3"
End Sub
Finally, in the Worksheet module for Sheet1:
Private Sub ComboBox1_Change()
Application.Run Sheet1.ComboBox1.Value
End Sub
Useful links to help files:
Application.Run Method
Ron de Bruin - How do I use Application.Run in Excel