VBA 255 Maximum Characters in 1 Cell - excel

I have a simple macro which adds the contents of each row in an excel sheet to a text file, with delimiters in between each cell value. This is done by running a for loop which iterates through each row and at the end of each iteration the values are added to the end of a String variable.
Each ROW can have a lot of characters in it - I have not noticed any issues with that. However, when 1 single cell contains more than 255 characters, the concatenation fails. I am not sure if it is because of String limitations (I don't think that is the case), or if it is the Trim, Join, or Index functions that contains this limitation, or if it something else. Any help in getting some more insight would be appreciated.
The line in question ('R' refers to the row/iteration number):
stringVariable = stringVariable & vbNewLine & Application.Trim(Join(Application.Index(Cells(R, "A").Resize(, 25).Value, 1, 0), "|"))
The error is:
Run-time error '13': Type mismatch

The problem is with the Application.Index. How to debug?
Let's have the active sheet with any values in row 1, all with less than 255 chars. But in one of this cells in row 1, for example in C1, should be the formula:
=REPT("c",255)
Now split the code into parts:
Sub test()
r = 1
v2DArray = Cells(r, "A").Resize(, 25).Value
index1DArray = Application.Index(v2DArray, 1, 0)
joinString = Join(index1DArray, "|")
stringVariable = Application.Trim(joinString)
MsgBox stringVariable
End Sub
This will work until you change the formula to =REPT("c",256). Now it will fail at Application.Index.
Instead of the Application.Index you could do the following:
Sub test2()
r = 1
v2DArray = Cells(r, "A").Resize(, 25).Value
ReDim v1DArray(LBound(v2DArray, 2) To UBound(v2DArray, 2)) As String
For i = LBound(v2DArray, 2) To UBound(v2DArray, 2)
v1DArray(i) = v2DArray(1, i)
Next
joinString = Join(v1DArray, "|")
stringVariable = Application.Trim(joinString)
MsgBox stringVariable
End Sub

After experimenting using different combinations of the already present functions, I found that the macro finishes without issues when Index is not used.
In the end I decided to add the value of each cell to the end of the string, one at a time. Solution:
For i = 1 To numberOfColumns
If i < numberOfColumns Then
stringVariable = stringVariable & Trim(Cells(R, i).Value) & "|"
Else
stringVariable = stringVariable & Trim(Cells(R, i).Value)
End If
Next i
stringVariable = stringVariable & vbNewLine

Related

INDEX and AGGREGATE Function in Excel VBA

I'm trying to move an in cell formula to VBA, because otherwise it's always recalculating, even when I deactivate the excel option, it comes back when I reopen the file. That's why I want to move that formula to VBA, where it happens only when I press a button, which is much smarter.
I have a master table with data, which I aggregate and index and express it on another sheet in a table. -> column A to S are in the master table, in the aggregated table, I will only have column A,C,E,G,H,I,J,K,L,M and P
The formula I want to move to VBA is the following:
=IFERROR(INDEX(Endkontrolle!$A:$S;AGGREGATE(15;6;ROW(Endkontrolle!$A:$S)/((FIND($B$3;Endkontrolle!$F:$F;1)>0)*(Endkontrolle!$S:$S="x"));ROW()-32)-0;1);"")
Can somebody help me translate that formula to VBA script?
thank you very much
Try this code:
Sub Button1_Click2()
'Declarations.
Dim RngTable As Range
Dim RngTarget As Range
Dim StrColumnsIndex As String
'A string is used to stores the index of the columns to be copied.
StrColumnsIndex = "1;3;5;7;8;9;10;11;14;15;16"
'RngTable is set as the range that will host the aggregated table.
Set RngTable = Sheets("Aggregated sheet").Range("A33:K34") '< EDIT THIS LINE ACCORDGLY TO YOU NEED
'Clearing RngTable.
RngTable.ClearContents
'Checking if StrColumnsIndex and RngTable are compatible.
If UBound(Split(StrColumnsIndex, ";")) + 1 <> RngTable.Columns.Count Then
MsgBox "The number of columns requested via StrColumnsIndex and the number of columns avaiable in RngTable do not match. Redefine the variables properly. The aggregated table will not be updated.", vbCritical + vbOKOnly, "Variable mismatch"
Exit Sub
End If
'Covering each cell in RngTable.
For Each RngTarget In RngTable
'The result is reported in each cell. The [row] element of the INDEX is obtained by subtracting _
RngTable.Row from the RngTarget.Row and adding one. This way each row is properly reported. The _
[col] element of the INDEX is obrained by splitting StrColumnsIndex using the difference between _
the RngTarget.Column and RngTable.Column as index. This way each requested column as listed in _
StrColumnsIndex is reported.
'RngTarget.Formula = "=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x""))," & RngTarget.Row - RngTable.Row + 1 & ")-0," & Split(StrColumnsIndex, ";")(RngTarget.Column - RngTable.Column) * 1 & "),"""")"
RngTarget.Value = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x""))," & RngTarget.Row - RngTable.Row + 1 & ")-0," & Split(StrColumnsIndex, ";")(RngTarget.Column - RngTable.Column) * 1 & "),"""")")
'If RngTarget contains nothing then it's assumed there are no more results to be reported and the macro is terminated.
If RngTarget.Value = "" Then Exit Sub
Next
End Sub
Thanks for that. I implemented it and it works for 1 row. If I want to add the next data set from the main table, that does only repeat the content from previous row. How can I achieve, that it lists me more than 1 line of aggregated data?
Expected result:
it picks the relevant rows of data and lists it (different data according the find criteria)
Actual result:
it picks only 1 row and repeats it for the second line
Now I defined following code:
Sub Button1_Click()
Cells(33, 1) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,1),"""")")
Cells(33, 2) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,3),"""")")
Cells(33, 3) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,5),"""")")
Cells(33, 4) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,7),"""")")
Cells(33, 5) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,8),"""")")
Cells(33, 6) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,9),"""")")
Cells(33, 7) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,10),"""")")
Cells(33, 8) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,11),"""")")
Cells(33, 9) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,14),"""")")
Cells(33, 10) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,15),"""")")
Cells(33, 11) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,16),"""")")
Cells(34, 1) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,1),"""")")
Cells(34, 2) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,3),"""")")
Cells(34, 3) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,5),"""")")
Cells(34, 4) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,7),"""")")
Cells(34, 5) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,8),"""")")
Cells(34, 6) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,9),"""")")
Cells(34, 7) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,10),"""")")
Cells(34, 8) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,11),"""")")
Cells(34, 9) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,14),"""")")
Cells(34, 10) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,15),"""")")
Cells(34, 11) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,16),"""")")
End Sub
Here's a sample of the data in the main table "Endkontrolle":
Date
Product
Employee
...
Date Range
22.04.2022
MOTI
AKAH
...
x
23.04.2022
MOTI_BG
AKAH
...
x
26.04.2022
MOTI
AKAH
...
On the reporting page, I would like to list down up to 20 rows of Data, which are in the Date Range ('x') from the "Endkontrolle" worksheet.
In the upper example, it should list row 1+2, but not 3.

Why does Excel suddenly find something in a cell?

I have a vba function that receives a row from a workbook and looks in come columns for data, setting variables or not depending on what is found. For rows 2-16, everything works fine; empty cells get skipped. Suddenly on row 17, a cell which seems empty triggers the first if-condition.
I've tried adding an additional check for cells that contain an empty string, and in the worksheet itself I checked CODE(H17) which was #VALUE
Function calcID(r As Long) As Variant
If (Not IsEmpty(allProps.Cells(r, 8))) Or (Not allProps.Cells(r, 8).Value = "") Then
MsgBox "Found ID: " & allProps.Cells(r, 8).Value & " in allProps row " & r
calcID = CDate(allProps.Cells(r, 8).Value)
ElseIf Not IsEmpty(allProps.Cells(r, 9)) Or Not allProps.Cells(r, 9).Value = "" Then
MsgBox "Found reverse ID: " & allProps.Cells(r, 9).Value & " in allProps row " & r
calcID = CDate(allProps.Cells(r, 9).Value)
Else
calcID = ""
End If
End Function
I use CDate elsewhere and it works fine. Ultimately the error shows up once I'm inside the if because I think CDate doesn't have a string to work with.
I think you only want to process the cells if they are both non-empty AND not equal to emptystring. This will prevent strange behavior if a cell is somehow not empty but is equal to emptystring.
Change your logical ORs to ANDs.
Function calcID(r As Long) As Variant
If (Not IsEmpty(allProps.Cells(r, 8))) And (Not allProps.Cells(r, 8).Value = "") Then
MsgBox "Found ID: " & allProps.Cells(r, 8).Value & " in allProps row " & r
calcID = CDate(allProps.Cells(r, 8).Value)
ElseIf Not IsEmpty(allProps.Cells(r, 9)) And Not allProps.Cells(r, 9).Value = "" Then
MsgBox "Found reverse ID: " & allProps.Cells(r, 9).Value & " in allProps row " & r
calcID = CDate(allProps.Cells(r, 9).Value)
Else
calcID = ""
End If
End Function
Some of the value in the cell which starts with = is taken as formula in excel.
For eg : =test is taken as a formula with error like this in excel.
I do had a similar issue and sorted it out by comparing the value in the cell and trimming off unwanted = in cell value.
Please make sure you are not getting any value in that cell which makes excel think that it is a formula or cell reference.

ALV grid only loads first 64 rows, how to change default load

Situation
I have created a lookup functionality for SAP GUI scripting.
If a grid row has specific values in specific columns, then it is double clicked (this triggers loading specific dependent data).
My grid has less than 300 rows, so loading so much data shouldn't strain a modern computer.
Issue
The issue I have is that from SAPGrid Row 64 it returns "" for each cell. If I enter debugging and scroll down in the ALV grid then the grid row loads and the results are found.
Possible solutions
Can I change how many rows are loaded on default?
Is there a method for pulling the full recordset?
The alternative options include scrolling up and down using scripting or setting up filters.
Code
Sub FindGridLine(SAPGrid As Object, criteria() As String)
SAPGrid.ClearSelection 'first it deselects what has been selected
For k = 0 To (SAPGrid.RowCount - 1) 'for each grid row
For i = 1 To UBound(criteria, 1) ' for each criteria row except for the first (should be only 1)
For j = LBound(criteria, 2) To UBound(criteria, 2) 'and for each column
tempstr = SAPGrid.GetCellValue(k, criteria(0, j))
If tempstr <> criteria(i, j) Then 'if the criterion doesn't match
GoTo nextrow 'then go to the next row
End If
Next j
Next i
'if it passed the criteria then doubleclick it
SAPGrid.DoubleClick k, criteria(0, 0)
Exit Sub
nextrow:
Next k
'in case no results were found
MsgBox "No line was found in grid!"
End Sub
Updated code
Code updated based on correct answer from #Asger.
Since lookups mostly work with primary keys, I went for the safe solution of SAPGrid.GetCellValue(k, criteria(0, j)) = "" but the solution is in fact SAPGrid.SetCurrentCell k, criteria(0, j).
Sub FindGridLine(SAPGrid As Object, criteria() As String)
' SAPGrid.SelectAll 'first it selects everything as to load the full grid
SAPGrid.ClearSelection 'first it deselects what has been selected
For k = 0 To (SAPGrid.RowCount - 1) 'for each grid row
For i = 1 To UBound(criteria, 1) ' for each criteria row except for the first (should be only 1)
For j = LBound(criteria, 2) To UBound(criteria, 2) 'and for each column
tempstr = SAPGrid.GetCellValue(k, criteria(0, j))
If tempstr = "" Then SAPGrid.SetCurrentCell k, criteria(0, j) 'this solution only works if the search is done in a non-empty field
tempstr = SAPGrid.GetCellValue(k, criteria(0, j))
If tempstr <> criteria(i, j) Then 'if the criterion doesn't match
GoTo nextrow 'then go to the next row
End If
Next j
Next i
'if it passed the criteria then doubleclick it
SAPGrid.DoubleClick k, criteria(0, 0)
Exit Sub
nextrow:
Next k
'in case no results were found
For i = 0 To UBound(criteria, 1) ' for each criteria row except for the first (should be only 1)
For j = LBound(criteria, 2) To UBound(criteria, 2) 'and for each column
tempstr = tempstr & "|" & criteria(i, j)
Next j
If i <> UBound(criteria, 1) Then
tempstr = tempstr & vbNewLine
End If
Next i
MsgBox "No line was found in grid!" & vbNewLine & "Please select line" & tempstr & vbNewLine & "manually and press 'OK'" & vbNewLine & "or enter debug mode."
End Sub
GuiGridView / ALV Grid Control: For large amounts of data, reloading of the content takes place only after scrolling, otherwise it is likely that only one empty string will be returned as the result - even without causing an exception.
Therefore SetCurrentCell should always be used to focus and load the dataset to be read.
Please test e. g. SAPGrid.SetCurrentCell(k, 1)
Maybe it's sufficient to load every new 64 rows (I can't test it):
If k Mod 64 = 63 Then ' at least if 1 row before each 64 rows
SAPGrid.SetCurrentCell (k, criteria(0, LBound(criteria, 2)))
End If

Adding freshly created formula into new module

I've just created a brand new macro. Took function down below from internet (all credits goes to trumpexcel.com), code down below
Function CONCATENATEMULTIPLE(Ref As Range, Separator As String) As String
Dim Cell As Range
Dim Result As String
For Each Cell In Ref
Result = Result & Cell.Value & Separator
Next Cell
CONCATENATEMULTIPLE = Left(Result, Len(Result) - 1)
End Function
Then I proceed to extract data from various columns and into the one (my table is 20 rows x 10 columns)
Sub conact_data()
Dim i As Integer
For i = 2 To Cells(Rows.Count, "A").End(xlUp).Row
Cells(i, "M").Value = Cells(i, "A").Value & " " & _
Cells(i, "B").Value & " / " & Cells(i, "D").Value & "; "
Next i
End Sub
Thanks to that I've got combined data from column A, B and D, so its 20 rows. All I want to do now is to concatenate data from M2:M21 using CONCATENATEMULTIPLE function therefore I try various approach (I want this huge line in P2 cell) like :
Cells(2, 16).Value = CONCATENATEMULTIPLE (M2:M21, " ")
or
Range("P2") = "CONCATENATEMULTIPLE (M2:M21, " ")"
I don't really know how to apply that
Secondly, I'd like withdraw the Cells(i, "B").Value as percentage. Can I do that in one line like Cells(i, "B").NumberFormat="0.00%".Value (which is not working for me obviously) else I need to copy column B into another column with number format and then combine the new column, properly formatted instead of column B?
Thanks in advance
Percent format: Range("B" & i).NumberFormat = "0.00%"
CONCATENATEMULTIPLE
In VBA, CHR(32) = " "
In Excel, CHAR(32) = " "
With that being said...
'Value
Range("P2").Value = CONCATENATEMULTIPLE(Range("M2:M21"), CHR(32))
'Formula
Range("P2").Formula = "=CONCATENATEMULTIPLE(M2:M21, CHAR(32))"
You should really qualify all of your ranges with a worksheet
Say your workbook has 10 sheets. When you say Range("P2"), how do we (VBE) know what sheet you mean? Objects need to be properly qualified. Sometimes this is not a huge issue, but when you are working across multiple sheets, not qualifying ranges can lead to some unexpected results.
You can qualify with a worksheet a few ways.
Directly: ThisWorkbook.Sheets("Sheet1").Range("P2").Copy
Or use a variable like so
Dim ws as Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
ws.Range("P2").Copy
Now there is no room for ambiguity (potential errors) as to the exact location of Range("P2")
First of all, remove your ConcatenateMultiple() code, and instead use Excel worksheet function CONCAT(), which takes a range and a delimiter as parameters.
Here is how you can handle the percentage issue and supply a default for non-numeric items. I've also cleaned up the way you reference your data range.
Sub concat_data()
Dim rngRow As Range, vResult As Variant
Const DEFAULT = 0 'Can also be set to a text value, eg. "Missing"
For Each rngRow In [A2].CurrentRegion.Rows
If IsNumeric(rngRow.Cells(, 4)) Then vResult = rngRow.Cells(, 4) * 100 & "%" Else vResult = DEFAULT
Range("M" & rngRow.Row) = rngRow.Cells(, 1) & rngRow.Cells(, 2) & "/" & vResult & ";"
Next
[M2].End(xlDown).Offset(1).Formula = "=CONCAT(M2:M" & [M2].End(xlDown).Row & ",TRUE,"" "")"
End Sub
I'm not a fan of hard-coding range references, like the [A2] or Range("M"), but will leave that for another time.

Excel vba - how to find digit no 8 in cell and evaluate if it's the correct one

Got this vba so far:
For Each cell In Range("A2", Range("A" & Rows.Count).End(xlUp))
If Len(cell.Value) = 11 Then
cell.Copy Destination:=Range("B" & Rows.Count).End(xlUp).Offset(1, 0)
End If
Next cell
I need to add some code behind "11" in the second line, but I can't figure out how to write it. What I want to do is to add something like "and digit number 8 (of 11) is 1 or 3 or 5 or 7 or 9 then".
Anyone that can help me out?
Try this
For Each cell In Range("A2", Range("A" & Rows.Count).End(xlUp))
If Len(cell.Value) = 11 Then
Select Case Mid(cell.Value, 8, 1)
Case 1, 3, 5, 7, 9
cell.Copy Destination:=Range("B" & Rows.Count).End(xlUp).Offset(1, 0)
End Select
End If
Next cell
I couldn't follow your explanation but according to your title, you need to check if the 8th character is a string (from a cell, etc) is "the right one":
Function Check8thCharacter(myString As String, theRightOne As String) As Boolean
Check8thCharacter = (Mid(myString, 8, 1) = theRightOne)
End Function
Here's an example of it in use:
Sub Digit8()
Range("A1") = "ABCDEFGHIJKLM"
Const testRightOne = "H"
If Check8thCharacter(Range("A1"), testRightOne) Then
MsgBox "It's the right one!"
Else
MsgBox "It's the wrong one."
End If
End Sub
Another look at your question makes me wonder if you're actually trying to determine whether the eighth character of a cell is an odd number, which is only a slight variation:
Function Is8thOdd(rg As Range) As Boolean
On Error Resume Next 'error will also return false
Is8thOdd = (Mid(rg, 8, 1) / 2 <> Mid(rg, 8, 1) \ 2)
End Function
Here's an example of it in use:
Sub Digit8()
If Is8thOdd(Range("A1")) Then
MsgBox "Yes, it's odd"
Else
MsgBox "No, it's not odd."
End If
End Sub

Resources