Nested ifs in vba - vba

I am new to vba and have only been using it for a couple of months. I've basically been learning as I go. Despite that, I'm trying to write some code to care for a variety of functions. I have written the code below that is launched from a command button on a userform. The code basically is supposed to search a row in an Excel worksheet and verify a couple of pieces of information and then take action. If the code cannot verify a match between entries on the row and entries in the userform, it stops and displays an error message. If it can verify the information match, it is supposed to proceed with populating some information on that row. I realize that this code I've written is probably completely hamfisted and decidedly un-elegant, however it was working until I added the validation for the product code. Please, can someone help? I've looked and looked and cannot find a solution.
Here is the code:
Private Sub AddDelivButton_Click()
Sheets("Deliveries").Activate
Dim number As Integer, rownumber As Integer, result As Long, i As Integer
number = POTextBox.Value
rownumber = 0
result = 1000000
For i = 1 To 25000
If Cells(i, 1).Value = number Then
result = Cells(i, 1).Value
rownumber = i
End If
Next i
If result = 1000000 Then
MsgBox "PO Number Not Found"
Sheets("Dashboard").Activate
Exit Sub
Else
Cells(rownumber, 1).Select
ActiveCell.EntireRow.Cells(3).Select
If ActiveCell.Value <> ProdCodeListBox1.Value Then
ActiveCell.EntireRow.Cells(5).Select
If ActiveCell.Value <> ProdCodeListBox1.Value Then
ActiveCell.EntireRow.Cells(7).Select
If ActiveCell.Value <> ProdCodeListBox1.Value Then
MsgBox "Product Code Not Found"
Sheets("Dashboard").Activate
Exit Sub
Else
ActiveCell.EntireRow.Cells(10).Select
If ActiveCell.Value = "" Then
ActiveCell.Value = ProdCodeListBox1.Value
ActiveCell.EntireRow.Cells(11).Value = WeightTextBox1.Value
ActiveCell.EntireRow.Cells(12).Value = DateTextBox1.Value
Else
ActiveCell.EntireRow.Cells(13).Select
If ActiveCell.Value = "" Then
ActiveCell.Value = ProdCodeListBox1.Value
ActiveCell.EntireRow.Cells(14).Value = WeightTextBox1.Value
ActiveCell.EntireRow.Cells(15).Value = DateTextBox1.Value
Else
This goes on for several iterations and to save space I have not included all of them here. Suffice it to say that the last two if statements were working until I added the validation for the ProdCodeListBox1.
Any help would be very much appreciated! Even if it is something simple I am overlooking.
Thanks!

In your current code you check cells 3, 5 and 7 for a matching value and display an error if none of them match then exit the Sub altogether. You only go on to check cell 10 if cell 7 has a match. If cells 3 or 5 match you never get to the check on cell 10
Try this instead:
ActiveCell.EntireRow.Cells(3).Select
If ActiveCell.Value <> ProdCodeListBox1.Value Then
ActiveCell.EntireRow.Cells(5).Select
If ActiveCell.Value <> ProdCodeListBox1.Value Then
ActiveCell.EntireRow.Cells(7).Select
If ActiveCell.Value <> ProdCodeListBox1.Value Then
MsgBox "Product Code Not Found"
Sheets("Dashboard").Activate
Exit Sub
End If
End If
End If
ActiveCell.EntireRow.Cells(10).Select
If ActiveCell.Value = "" Then
All of the ActiveCell and Select business isn't the best way to get the values from specific cells but that's a different question

Related

Not able to activate the worksheets from the userform

Now, I am trying to run my code and suddenly I am getting an error in my Worksheet activation line. Yesterday, it worked well without any problems. Until now I was not able to find the mistake. Please refer to the code below. I'd appreciate if anyone can give me a suggestion on how to run the code without getting an error.
For the below commandbutton I am getting the error when clicked.
Private Sub cmdsubmit_Click()
Dim i As Integer
Dim Submittedtask As String
Worksheets("Submittedtask").Activate 'this line i am getting error
'position cursor in the correct cell A8.
ActiveSheet.Range("A8").Select
i = 1 'set as the first ID
'validate first three controls have been entered...
If Me.txtProject.Text = Empty Then 'Firstname
MsgBox "Please enter firstname.", vbExclamation
Me.txtProject.SetFocus 'position cursor to try again
Exit Sub 'terminate here - why continue?
End If
'if all the above are false (OK) then carry on.
'check to see the next available blank row start at cell A2...
Do Until ActiveCell.Value = Empty
ActiveCell.Offset(1, 0).Select 'move down 1 row
i = i + 1 'keep a count of the ID for later use
Loop
'Populate the new data values into the 'Data' worksheet.
ActiveCell.Value = i 'Next ID number
ActiveCell.Offset(0, 2).Value = Me.txtProject.Text 'set col B
ActiveCell.Offset(0, 3).Value = Me.txtEnia.Text 'set col C
ActiveCell.Offset(0, 1).Value = Me.DTPicker1.Value
'Clear down the values ready for the next record entry...
Me.txtProject.Text = Empty
Me.txtEnia.Text = Empty
Me.cboLs.Set Focus 'positions the cursor for next record entry
End Sub

ActiveCell.EntireRow.Delete

I have this VBA code to delete rows in excel
Sub deleterows()
i = 1
Do Until i = 150000
If ActiveCell.Value = False Then
ActiveCell.EntireRow.Delete
End If
ActiveCell.Offset(1, 0).Activate
i = i + 1
Loop
End Sub
However this code is not deleting all the rows that contain the "False" value, I've been trying to change it to activecell.value="" and activecell.value=vbnullstring but still it does not deletes all blank rows
You should move from the last row to the top, if you're deleting rows.
Also, it's best to avoid using ActiveCell.
Sub deleterows2()
i = 1
For i = 150000 To 1 Step -1
If Cells(i, 1).Value = False Or Cells(i, 1).Value = "False" Then
Rows(i).EntireRow.Delete
End If
Next i
End Sub
Tweak as needed. I'm assuming your column A has the cells you're checking for. If it's another column, just use that column's index number in the Cells(i,1). So if you need to check column D, use Cells(i,4)
You can fix it with a small change as follows:
If ActiveCell.Value = False Then
ActiveCell.EntireRow.Delete
Else
ActiveCell.Offset(1, 0).Activate
End If
Basically, you should only activate the next sell when the value is != False, otherwise it will skip rows.
Here's a handful of nice things baked in to what it looks like what you want to accomplish.
I'm assuming that 150000 is basically just a big number so that you are confident that all used rows are being considered.
Sub DeleteRows()
i = 0
Do While ActiveCell.Offset(i, 0).Row <= ActiveCell.End(xlDown).Row
'This only considers used rows - much better than considering 15,000 rows even if you're only using 100
If ActiveCell.Offset(i, 0).Value <> 1 Then
'If you directly have a boolean value (i.e. 'True', 'False', or '0','1', you do not need to compare to another value. If your cells contain text, compare with the string in quotes (i.e. ...= "False")
ActiveCell.Offset(i, 0).Delete
Else: i = i + 1
End If
'Don't have to activate the next cell because we're referencing off of a fixed cell
Loop
End Sub

How to write a VB script to find multiple keywords within all cells and highlight each keyword?

I'm currently having a list of keywords (e.g. CFO, CTO, interim manager, etc.) and I want to have a macro assigned to a button that can search all cells in Column E of Sheet 1 which contain these keywords then give back the result as well as highlight the keyword in the cell.
Each keyword is in a separate cell in Column A of Sheet 2.
If there is one keyword in the list, it will search for one but if there are more, it will search for combination.
Here is the screenshot to illustrate what I've describe above
I have found something over the Internet with suggestion to use AutoFilter but I can only use it to perform a search for one keyword. This is what I've tried:
Sub EmailFilter()
Application.ScreenUpdating = False
With Worksheets("Sheet1").Columns("E:E")
.AutoFilter Field:=1, Criteria1:= _
"=*" & Worksheets("Sheet2").Range("A2:A10") & "*", Operator:=xlAnd
End With
Application.ScreenUpdating = True
End Sub
Thanks in advance.
The below code will color all the matches with the same color(I have chosen blue). You can write this macro in a module and then create a Form Control Button and assign the macro to the button.
Sub macro()
Dim a As Integer, x As String, mystring As String
a = 2
Sheets("Sheet2").Activate
Cells(a, 1).Activate
Do While ActiveCell.Value <> ""
x = ActiveCell.Value
p = Len(x)
Application.GoTo Sheet1.Range("E2")
Do While ActiveCell.Value <> ""
mystring = ActiveCell.Value
If InStr(mystring, x) > 0 Then
Position = InStr(1, mystring, x)
If Position > 0 Then
ActiveCell.Characters(Position, p).Font.Color = RGB(255, 0, 0)
End If
End If
ActiveCell.Offset(1, 0).Activate
Loop
a = a + 1
Application.GoTo Sheet2.Cells(a, 1)
Loop
End Sub
Let me know if you have any other specific requirements so that the code can be altered. I hope this helps.

What is the difference between .Value = "" and .ClearContents?

If I run the following code
Sub Test_1()
Cells(1, 1).ClearContents
Cells(2, 1).Value = ""
End Sub
When I check Cells(1, 1) and Cells(2, 1) using formula ISBLANK() both results return TRUE. So I'm wondering:
What is the difference between Cells( , ).Value = "" and Cells( , ).ClearContents?
Are they essentially the same?
If I then run the following code to test the time difference between the methods:
Sub Test_2()
Dim i As Long, j As Long
Application.ScreenUpdating = False
For j = 1 To 10
T0 = Timer
Call Number_Generator
For i = 1 To 100000
If Cells(i, 1).Value / 3 = 1 Then
Cells(i, 2).ClearContents
'Cells(i, 2).Value = ""
End If
Next i
Cells(j, 5) = Round(Timer - T0, 2)
Next j
End Sub
Sub Number_Generator()
Dim k As Long
Application.ScreenUpdating = False
For k = 1 To 100000
Cells(k, 2) = WorksheetFunction.RandBetween(10, 15)
Next k
End Sub
I get the following output for runtime on my machine
.ClearContents .Value = ""
4.20 4.44
4.25 3.91
4.18 3.86
4.22 3.88
4.22 3.88
4.23 3.89
4.21 3.88
4.19 3.91
4.21 3.89
4.17 3.89
Based on these results, we see that the method .Value = "" is faster than .ClearContents on average. Is this true in general? Why so?
From what I have found, if your goal is to simple have an empty cell and you do not want to change anything about the formatting, you should use Value = vbNullString as that is the most efficient.
The 'ClearContents' is checking and changing other properties in the cell such as formatting and the formula (which is technically a separate property than Value). When using Value = "" you are only changing one property and so it is faster. Using vbNullString prompts the compiler that you are using an empty string versus the other way with double quotes, it is expecting a general string. Because vbNullString prompts it to expect an empty string, it is able to skip some steps and you get a performance gain.
when apply both in single cell I don't think there is no any sensible deferent but when you apply it in range Range("A1:Z1000").ClearContents is easier and faster than use cell(i,j).value="" in nested loop or one for loop
I did find one difference that might be of note. ClearContents returns a value, which seems to be of type Boolean in my limited testing (docs mention Variant type).
Option Explicit
Public Sub ClearA1()
Dim a As Range
Dim b As Boolean
Set a = Range("A1")
Debug.Print b 'It's False, the default value
b = a.ClearContents
Debug.Print b 'Set to True, as the action was completed
End Sub
I'd guess some of the overhead is from the fact ClearContents does return a value, where you are just setting a value property in the alternate case.
Ultimately, in terms of outcomes of setting the value both methods appear functionally the same.
Using clearcontent has different behavior on cells with formulas.
When you have just one value, the behavior is the same, but differs when you have formulas in it.
You can notice a big difference in Excel spreadsheet.
Assume that B1 is filled by equation returns blank
A1 = 5
B1 = "=if(A1=5,"","x")
In this case, you have to equations that you can write in C1
(1) C1 = <=isblank(B1)>
(2) C1 =
Solution 1 will return false, as the cell is filled with equation
Solution 2 will return True
I came across this topic a little late, but i would like to share what i have noticed with abit of code of mine, i don't think i can fully explain it but ill do my best.
For Each Cell In ws.Range("D12:D161") 'Order feed colom
Select Case Cell.Value
Case 0
Cell.Interior.Color = Cell.Offset(0, -1).Interior.Color
Case 1
Cell.Interior.Color = 10198015
Case 2
Cell.Interior.Color = 11854022
End Select
Cell.value = ""
Next Cell
This is a bit of code that i have used in order to clear some fields and give some color to the range D12:D161. Nothing special here, If the value is 0 then copy your neighbor if 1 then red if 2 then green. And clear the cell afterwards
But in order for this code to run it took roughly 5-6 seconds for me, which i thought was a fair bit for a small piece of code. Plus i used this on a Private Sub Workbook_SheetActivate(ByVal Sh As Object) which made it for the user unpleasant to wait 5-6 seconds for a screen transition. So i put a loop in to check for empty's in a row and then skip out.
It is noteworthy that this is part of a script, and yes i have my screenupdating off, calculations off, events off during this piece of code.
For Each Cell In ws.Range("D12:D161") 'Order feed colom
Select Case Cell.Value
Case 0
Cell.Interior.Color = Cell.Offset(0, -1).Interior.Color
Erow = Erow +1
Case 1
Cell.Interior.Color = 10198015
Erow = 0
Case 2
Cell.Interior.Color = 11854022
Erow = 0
End Select
Cell.value = ""
if Erow = 10 then exit for
Next Cell
Now instead of having to do 149 rows i did roughly 58 rows, depending on my data in the column. But still it it took 3-4 seconds in order to fully run. During Debug mode i noticed no lag at all. If i manually ran the code when already on the sheet, there was 0 delay. Almost instant, after testing abit more but when using a Private Sub Workbook_SheetActivate(ByVal Sh As Object) with this code it still ran 3-4 seconds.
After testing individual rows of code, i came across the .Value = "". Removing this line from the code made it run 0,5 seconds.... So now i knew where my problem was, using multiple ways of emptying my cells. I noticed that .clearcontents was the fastest for me. Apparently if you move from Sheet to sheet EVENTHOUGH ws. has been declared as my active sheet, it just ook alot of time
For Each Cell In ws.Range("D12:D161") 'Order feed colom
Select Case Cell.Value
Case 0
Cell.Interior.Color = Cell.Offset(0, -1).Interior.Color
Erow = Erow +1
Case 1
Cell.Interior.Color = 10198015
Erow = 0
Case 2
Cell.Interior.Color = 11854022
Erow = 0
End Select
Cell.ClearContents 'DONT USE .Value = "", makes the code run slow
if Erow = 10 then exit for
Next Cell
In conclusiong.
Using the above code with
.value = "" took 4-5 seconds
.value = VbNullstring took 3-4 seconds
.ClearContents took only 0,5 seconds. But only during a worksheet transition
Private Sub Workbook_SheetActivate(ByVal Sh As Object)
If anybody is able to explain why this is or what exactly is going on, i would appreciate it.

Excel VBA: Can't get a match, error "Unable to get the Match property of the WorksheetFunction class"

For the love of all that is good, I cannot seem to get this to work. I keep getting the error mentioned above.
I have this table, and I'm trying to find out whether the code matches it's own sub-code somewhere within the other column, however it's erroring out. Your help is greatly appreciated.
Sub testing()
Dim m1 As long
Dim myrange As Range
Set myrange = Worksheets("Sheet1").Range("B2:B23")
For e = 2 To 23
m1= Application.WorksheetFunction.Match(Cells(e, 1).Value, myrange, 0)
If m1 > 0 Then
Cells(e, 3).Value = "Yes"
Else
Cells(e, 3).Value = "No"
End If
Next e
MsgBox "Complete!"
End Sub
Use the Application.Match function which allows for better ability to trap errors. When using the WorksheetFunction.Match, when a match is not found, it returns an error, which is what you're experiencing.
If Not IsError(Application.Match(Cells(e, 1).Value, myrange, 0)) Then
'Do stuff when the match is found
Cells(e, 3).Value = "Yes"
Else:
Cells(e, 3).Value = "No"
End If
You could also potentially use the CountIf function:
If Application.WorksheetFunction.CountIf(myRange, Cells(e,1).Value) > 0 Then
Cells(e,3).Value = "Yes"
Else:
Cells(e,3).Value = "No"
End If
Neither of these approaches requires you to use the m1 variable, you can assign this variable within the True part of the If/Then statement, if you need to identify where the match is found.
Just as another option, this can also be done by putting the formula below in cell C2, and dragging it down to C23.
=IF(COUNTIF($A$2:$A$23,B2)>=1,"YES","NO")