Couldn't find this question on StackOverflow, so my apologies when not searched well enough...
I have the following code (simplified)
Sub Task_1()
"copy stuff from sheet2 to sheet1"
End Sub
Sub Task_2()
"Print sheet1"
End Sub
Sub Task_3()
"Do ordernumber +1"
End Sub
Now I want to loop this. So after sub Task_3, I want to call Task_1 again untill a certain cell is empty. I have the following, but not sure what to put into the questionmark.
Sub Start_orderprint()
Call Task_1
Call Task_2
Call Task_3
If Sheet1.Range("A4").Value <> Empty
Then ?????
Else
msgbox "Finished"
exit sub
End if
End sub
A Do...Loop to Run Procedures
Do...Loop statement
Using Do...Loop statements
But be careful because this may end up being an endless loop (if it never becomes Empty).
Option Explicit
Sub Start_orderprint()
Do Until Sheet1.Range("A4").Value = Empty
' Or
'Do While Sheet1.Range("A4").Value <> Empty
Task_1
Task_2
Task_3
Loop
MsgBox "Finished"
End Sub
In the previous example, if the value is initially Empty, the loop will never be entered. If you want to enter it once, no matter what, you will have to use the following:
Sub Start_orderprint()
Do
Task_1
Task_2
Task_3
Loop Until Sheet1.Range("A4").Value = Empty
' Or
'Loop While Sheet1.Range("A4").Value <> Empty
MsgBox "Finished"
End Sub
Related
I am trying to delete cells in a table.
The worksheet line is marked as debug.
but i can't find my mistake...
Code:
Sub clear_click()
If MsgBox("Del?", vbOKCancel) = vbCancel Then
Exit Sub
Else
Worksheets("calc").Range("G9:G12").ClearContents
End If
End Sub
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 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
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 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