Just a quick question today, I am new to VBA and I am currently writing some code. In the code I use GoTo not for errors but just to write two different parts of functionality. For example the below:
Sub run_FetchTradeFilter()
Dim EntrySelection As Range
Set EntrySelection = Selection
[EntireTradeQuery].Calculate
If [CustomQuery] = True Then GoTo CaseCustomQuery Else GoTo CaseCustomList
CustomQuery:
'fill
'with
'ccde A
CustomList:
'fill
'with
'ccde B
EntrySelection.Select
End Sub
My question is, should I be using GoTo if its not for an error. Or should I be using an if statement or should I be using a SwitchCase?
Great, thanks to both for the clarification! Bets to avoid GoTo. Therefore I will write 2 separate functions and call them in the If statement
Related
I was wondering how I would call something in VBA to write its code while running? So I mean if I had the text in A1 read:
sub Write()
Call OtherScript
End Sub
So again that is text inside the cell not in a VBA script. And then in a script while its running it Calls "A1" and the code that's in A1 gets run through VBA without having to actually put it in there.
This is not a real code obviously, I am really just trying to find out if this is possible. A friend that helps me learn to code and works me through a lot of VBA's said he does not know how that would work so Im posting it here to see if possible.
Please, try the following code. Before running it, write in a cell:
sub WriteSomething()
Call OtherScript
End Sub
You cannot create a function/sub named Write because this word is illegal, meaning something else in VBA.
and in the next cell (on the same row):
sub OtherScript()
MsgBox "Hello!"
End Sub
I used "K2". Use it too, or adapt the range from the code. You should also have a Module3 standard module. Please, update the module name with the one existing in your vbProject. Anyhow, the code can also create the module...
Copy the next code and run it:
Sub write_Run_Subs()
'It needs a reference to 'Microsoft Visual Basic For Applications Extensibility x.x'
Dim vbProj As VBProject, objMod As VBComponent, mdlName As String
Dim rngStr As Range, strSub1 As String, strSub2 As String
Set rngStr = Range("K2")
strSub1 = rngStr.value
strSub2 = rngStr.Offset(0, 1).value
mdlName = "Module3" 'of course, it have to exist in ThisWorkbook vbProject
Set vbProj = ThisWorkbook.VBProject
Set objMod = vbProj.VBComponents(mdlName)
objMod.CodeModule.AddFromString strSub1
objMod.CodeModule.AddFromString strSub2
Application.Run mdlName & ".WriteSomething"
End Sub
It is only a simple code without too much error handling, but it should work... If you run it twice, it will insert two such subs, if not preliminarily check their existence.
If adding the necessary reference looks complicated, please firstly run the following code, which will add it:
Sub addExtenssibilityReference()
'Add a reference to 'Microsoft Visual Basic for Applications Extensibility 5.3':
ThisWorkbook.VBProject.References.AddFromGuid _
GUID:="{0002E157-0000-0000-C000-000000000046}", _
Major:=5, Minor:=3
End Sub
When using Range.SpecialCells with a range that doesn't contain cells that match the criteria an error is thrown saying that no cells were found.
The most common solution to this issue is letting it happen and using an error handler to deal with it.
Is that the best known way to solve it or is there other solutions that might be as good or better which avoid using an error handler?
The only thing I can think of would be saving the first cell's value, then changing its value to one that matches the criteria so it avoids the error making it always match at least that one cell, then change the value back to its original value and check the matched range's address to see if it matched that one cell only or more.
A bad/slow solution would be to not make use of it at all and just use loops with checks.
Here's some simple sample code to demostrate a little how it works with the error handler:
Private Sub Procedure()
Dim OriginalRange As Excel.Range
Dim NewRange As Excel.Range
Set OriginalRange = ThisWorkbook.Worksheets(1).Range("A1:C4")
On Error GoTo ErrorHandler
Set NewRange = OriginalRange.SpecialCells(Type:=Excel.XlCellType.xlCellTypeConstants, Value:=Excel.XlSpecialCellsValue.xlNumbers)
Exit Sub
ErrorHandler:
If (VBA.Err.Number <> 1004) Then VBA.Err.Raise VBA.Err.Number
End Sub
Yes it is perfectly normal (I prefer this way) to use the error handler. What I do is, I sandwhich it between On Error Resume Next and On Error GoTo 0 and then check If NewRange is Nothing
See this example
On Error Resume Next
Set NewRange = OriginalRange.SpecialCells(Type:=Excel.XlCellType.xlCellTypeConstants, _
Value:=Excel.XlSpecialCellsValue.xlNumbers)
On Error GoTo 0
If NewRange Is Nothing Then
MsgBox "Your message here informing the USER that desired cells were not found"
Else
'
'~~> Do whatever you want with the range
'
End If
I'm working with excel vba since 3 month now and this is (after one course of programming in university) my first real contact to programming. Please take that to account.
I built up a userform with many textboxes. Therefore I wrote a makro which first checks if the user put in a value in every textbox so that afterwards the procedure begins. If there is not a value in every textbox I want the exit sub after msgbox the user to fill again every textbox. Quiet simple, right?
I thought the best way to manage this is using the Go to-statement. After showing my boss the code he told me I should never use this statement to avoid some sort of spaghetti code. He told me a real programmer would never use this statement and would try to work his way around. This is what my code looks like:
Private Sub SaveButton_Click()
Dim i As Integer
'mandatory textboxes:
For i = 1 To 13
If UserForm1.Controls("Textbox" & i) = "" Then: GoTo again
Next
'procedure...
Exit Sub
again:
MsgBox "Please fill in every mandatory textbox"
End Sub
My question: is it right to avoid this statement in every situation? Is it really some sort of unspoken rule to never use that statement? What are the Pros and Cons of this, and which are my alternatives(especially in this case)?
I appreciate every helpful answer. Thank you!
Your code can be easily re-written as below:
Private Sub SaveButton_Click()
Dim i As Integer
'mandatory textboxes:
For i = 1 To 13
If UserForm1.Controls("Textbox" & i) = "" Then
MsgBox "Please fill in every mandatory textbox"
Exit Sub
End If
Next
End Sub
Don't ever use GoTo unless it is behind On Error … or not avoidable. If there is any chance to avoid GoTo, then avoid it. It makes your code hard to maintain and is considered to be a bad practice.
As GSerg pointed out there might be rare cases where GoTo cannot be avoided. Eg. using GoTo for emulating missing language constructs (e.g. VBA lacks the Continue keyword) and exiting deeply nested loops prematurely.
Could be rewritten thus. So below the goto is replace by an Exit For and then a subsequent test. Avoid goto unless in an On Error Goto <lable> statement.
Private Sub SaveButton_Click()
Dim i As Integer
Dim bGut As Boolean: bGut = True
'mandatory textboxes:
For i = 1 To 13
If UserForm1.Controls("Textbox" & i) = "" Then
bGut = False
Exit For '* skip out
End If
Next
If Not bGut Then
MsgBox "Please fill in every mandatory textbox"
Else
'* start processing
End If
End Sub
Post has been Updated below original post
I am working with two tables and want to have them connected however, the first section contains more values than the second one. I was able to work that out by adding an IfError within the Evaluate function, seen from code example (1) to (2), (using help from If Error Then Blank)
(1)
Worksheets("Sheet1").Range("Cell1").Value = Worksheets("Sheet1").Evaluate("=INDEX(data,MATCH(value,lookup_column,FALSE),column)")
(2)
Worksheets("Sheet1").Range("Cell1").Value = Worksheets("Sheet1").Evaluate("=INDEX(data,MATCH(value,lookup_column,FALSE),column), Cell2")
However, I still would like a message saying that there was an error so I tried
Sub Name()
Application.ScreenUpdating = False
On Error GoTo Msg
Worksheets("Sheet1").Range("Cell1").Value = Worksheets("Sheet1").Evaluate("=INDEX(data,MATCH(value,lookup_column,FALSE),column), Cell2")
Worksheets("Sheet1").Range("Cell1").Value = Worksheets("Sheet1").Evaluate("=INDEX(data,MATCH(value,lookup_column,FALSE),column)")
Exit Sub
Msg: MsgBox "You've had a fatal error"
End
End Sub
It did not return a message, I am assuming this is because the code for VBA was written correctly and it was the Excel function code that had an error. So is there a way to use another function to determine the excel error?
This is a sub part of a larger coding so I know it is something that can be done in excel stand alone, but this is just a minor part of the whole. Thanks in advance.
UDATE:
With comments I was able remove the Evaluate function and replace the original code with the following:
Sub SetWaterfall ()
Application.ScreenUpdating = False
Dim vMatchVal As Variant
If Not IsError(vMatchVal) Then
vMatchVal = Application.Match(Sheets("Sheet1").Range("SelectLine"), Sheets("Sheet1").Range("AS8:AS34"), 0)
Worksheets("Sheet1").Range("AW45").Value = Application.Index(Sheets("Controls").Range("AR8:AR34"), vMatchVal)
Else
Worksheets("Controls").Range("AW45").Value = "Not Data"
MsgBox "First number not found"
End If
End Sub
The issue is still that the index/match functions returns a #NA error and the message box never appears.
(Help converting Index/Match function from Excel formula to VBA code https://www.mrexcel.com/forum/excel-questions/691904-translate-index-match-function-vba-code.html)
(If this edit process is not the correct procedure for let me know and I'll close the post)
In your revised code, you have the If Not IsError test preceding the assignment to the variable you're testing for error!
Let's fix that, and try some other refactoring (for legibility's sake). If this is still not working as expected, you're going to need to provide some example data which others can use to replicate the error.
Sub SetWaterfall()
' It's not necessary to disable ScreenUpdating for this procedure...
' Application.ScreenUpdating = False
Dim theSheet as Worksheet, controls as Worksheet
Dim vMatchVal As Variant
Dim lookupVal as String
Dim matchRange as Range, indexRange as Range
Set theSheet = Sheets("Sheet1")
Set controls = Sheets("Controls")
Set matchRange = theSheet.Range("AS8:AS34")
Set indexRange = controls.Range("AR8:AR34")
lookupValue = theSheet.Range("SelectLine").Value
vMatchVal = Application.Match(lookupVal, matchRange, 0)
If Not IsError(vMatchVal) Then
theSheet.Range("AW45").Value = Application.Index(indexRange, vMatchVal)
Else
controls.Range("AW45").Value = "Not Data"
MsgBox "First number not found"
End If
End Sub
The following code:
Sub lcm_()
If Selection.Areas.Count <> 1 Then Exit Sub
Dim a As Range
Set a = Selection.Areas(1)
a.Item(1).Value = Lcm(a.Item(1).Value, a.Item(2).Value)
End Sub
produces the error: Compile error: Sub or Function not defined, and I cannot figure out why. I have looked at similar problems, and it seems like the issue is that Excel cannot find the builtin function LCM, but the documentation makes it seem like it doesn't require any special libraries. I am very new to VBA, so it is entirely possible that this is something very simple, but I cannot figure out what.
EDIT:
It is worth noting that if I simply click on a cell and type =LCM(A8,A9), then it works fine.
Try this:
Sub lcm_()
If Selection.Areas <> 1 Then Exit Sub
Dim a As Range
Set a = Selection.Areas(1)
a.Item(1).Value = WorksheetFunction.Lcm(a.Item(1).Value, a.Item(2).Value)
End Sub