Loop through all cells in Range using Interop - vb.net

I want to loop through all cells in a range.
Dim rngTop, rngAll as Excel.Range
'Set a cell
rngTop = DirectCast(_sheet.Cells(1, 2), Excel.Range)
'Set a range from the Top cell to its last cell in the cells column
rngAll = rngTop.End(Excel.XlDirection.xlDown)
For Each cell As Excel.Range In rngAll
If cell.Value2 = "x" Then
'Do stuff
End If
Next
cell.Value is underlined and gives me the compil error that cell.value2 is an object and I cannot use an operator (= in this case) on it. Could anyone help me get this task accomplished? Value2 should not be an object.
I also tried:
Dim cell As Excel.Range = Nothing
Dim i As Integer
For i = 1 To rngAll.Rows.Count
If DirectCast(rngAll.Cells(i, 5), Excel.Range).Value2 = "x" Then
'Do stuff
End If
next i
but have the same problem as above.

I guess I have the solution. The problem here is that vb doesnt know what type value2 will deliver so it delivers an object. That is why the code give the message that the =-Operator cannot be applied. Using
If CStr(cell.value2) = "x" then
...
works perfect. So the best would be to write a function to check the valuetype for every possible type and convert it.

Related

Replace cell values in specific sheets with defined name

I am trying to run some code that replaces the cell values in a specific column with a defined name. In addition, I have a condition that the replacement should only take place if the first 9 characters of the values are xxxxxxxxx.
More precisely, it should change the values in C:C in 2 specific worksheets (I don't want to loop through the whole workbook).
I am not sure why nothing happens in the code (no error messages, nothing).
I presume, however, that I should not use With if I want the code to work in these 2 specific worksheets. I am also aware that my use of Range is probably not totally correct.
Sub ChangeMe()
Dim cl As Range
For Each cl In Worksheets("Sheet1").Range("C:C").End(xlUp)
With Worksheets("Sheet2").Range("C:C").End(xlUp)
If Left(cl.Value, 9) = "XXXXXXXXX" Then
cl.Value = ThisWorkbook.Names("MyDefinedName").RefersToRange
End If
End With
Next cl
End Sub
In answer your original questions:
I am not sure why nothing happens in the code (no error messages, nothing).
Nothing happens because your worksheet values are lowercase xxxxxxxxx, whilst your code checks for uppercase XXXXXXXXX.
I presume, however, that I should not use With if I want the code to work in these 2 specific worksheets.
Actually, you can use With with multiple sheets, as I will demonstrate below.
I am also aware that my use of Range is probably not totally correct.
That is true. If you were to fix the uppercase issue, only C1 would be changed. This is because .End() works on a single cell. If you supply a multi-cell range, it uses the top left most cell. So .Range("C:C").End(xlUp) is equivalent to .Range("C1").End(xlUp) which evaluates to just C1.
The following will answer your updated question:
Option Explicit
Public Sub ChangeMe()
Const l_xxxxxxxxx As String = "xxxxxxxxx"
Const l_MyDefinedName As String = "MyDefinedName"
Const s_Delimiter As String = ","
Const s_WorkSheetNames As String = "Sheet1,Sheet2"
Const s_ColumnToChange As String = "C:C"
Dim varWorkSheetName As Variant
For Each varWorkSheetName In Split(s_WorkSheetNames, s_Delimiter)
With Worksheets(varWorkSheetName).Range(s_ColumnToChange)
Dim rngCell As Range
For Each rngCell In .Resize(.Cells(Rows.Count).End(xlUp).Row)
With rngCell
Dim strCellValue As String: strCellValue = .Value2
If Left(strCellValue, Len(l_xxxxxxxxx)) = l_xxxxxxxxx Then
.Value2 _
= Names(l_MyDefinedName).RefersToRange.Value2 _
& Right$(strCellValue, Len(strCellValue) - Len(l_xxxxxxxxx))
End If
End With
Next rngCell
End With
Next varWorkSheetName
End Sub
Notes:
It is a good idea to use constants so all literal values are typed once only and kept grouped together.
Using .Value2, instead of .Value, is the recommended way to access a cell's value as it avoids implicit casting and is therefore faster. (Using .Value can also sometimes cause issues.)
Surprisingly, in VBA there are good reasons to put a variable declaration as close as possible to the first use of the variable. Two such reasons are 1) it improves readability, and 2) it simplifies future refactoring. Just remember that the variable is not reinitialised every time the Dim is encountered. Initialisation only occurs the first time.
If I understood your post correctly (which I doubt it), I think you want to loop through column "C" in both "Sheet1" and "Sheet2". Every cell that starts with 9 "XXXXXXXXX", should be replaced with the value in "MyDefinedName" Named Range.
Code
Option Explicit
Sub ChangeMe()
Dim cl As Range
Dim sht As Worksheet
For Each sht In ThisWorkbook.Sheets
With sht
If .Name = "Sheet1" Or .Name = "Sheet2" Then
For Each cl In .Range("C1:C" & .Cells(.rows.Count, "C").End(xlUp).Row)
If Left(cl.Value, 9) = "XXXXXXXXX" Then
cl.Value = ThisWorkbook.Names("MyDefinedName").RefersToRange
End If
Next cl
End If
End With
Next sht
End Sub
Let's imagine that this is your input:
In this case, you want to change the values in range A1:A2 to the value in C1 (named range xxxx123), because it starts with xxxx123. This is the code to achieve it:
Public Sub TestMe()
Dim myCell As Range
Dim myNamedRange As String
myNamedRange = "xxxx123"
For Each myCell In Range("A1:A2")
If Left(myCell, Len(myNamedRange)) = myNamedRange Then
myCell.Value = Range(myNamedRange)
End If
Next myCell
End Sub

Loop through Cols & Rows with IF statement vba

All,
I have written the below code to check if cells in the variable range have conditional formatting. However the code falls over at "If Cells.ColorIndex = 3 Then" can anyone suggest why the error is occurring and if there is a better solution than the below code to achieve a loop through cols & rows (variable length)
Sub Check_Conditional()
Dim rng As Range
Dim row As Range
Dim cell As Range
Dim RW As Long
RW = ActiveSheet.Range("Total").Offset(rowOffset:=-1).row
Set rng = Range("O7:AB" & RW)
For Each row In rng.Rows
For Each cell In row.Cells
If Cells.ColorIndex = 3 Then
MsgBox "Not all the cells have been filled out"
Exit For
End If
Next cell
Next row
End Sub
cell.ColorIndex is not a valid Range property.
If you mean to check the font's color then use If cell.Font.ColorIndex = 3 Then
If you mean to check the Fill color, then use If cell.Interior.ColorIndex = 3 Then
When you type in the editor, Cell. the VBA autocompletes it with the following options:
There's no cell.ColorIndex in the list:

Conditional Formatting Applying to a range instead of the individual cell

The following code is intended to format the cell
if the value entered is No and
If the value of the cell in same row and some columns before is listed in the sshDevices array
Function sshProblem(rng As Range) As Boolean
Dim portStatus As String
portStatus = rng.Value
Dim deviceType As String
deviceType = Cells(ActiveCell.Row, 3).Value
Dim sshDevices As Variant
sshDevices = Array("linux", "vmw", "docker", "unix")
If StrComp(portStatus, "No") = 0 Then
If StrComp(deviceType, sshDevices(1)) = 0 Then
sshProblem = True
End If
End If
End Function
For now I'm just comparing the value of deviceType against the second element in the array. The formatting is working properly if the device type matches and if the content is No. The problem is that when the content is No and the device type does not match, ALL of the cells in the column loose their format. For example:
I enter "No" in row 8 and the cell is properly formatted with pink background:
Then I do the same with columns 9 and 10:
But when I get to 10, I enter "No" in the cell but now the value of previous cell "ubuntu" does not match the value of position 1 of the array, so i would expect that cell not to have a pink background, however all previous cells loose their background when I enter "No":
The conditional formatting settings look like this (the column letter is 'I'):
I dont know if my problem is stating (rng As Range) or if it is the way I set up the conditional formatting rule. Ideas?
If sshProblem is in a regular module then any Cells/Range will refer by default to the ActiveSheet, not necessarily the sheet you're calling the function from.
Also, ActiveCell is no use here - if you want to reference the cell from where the formula is being called, use Application.Caller
Finally, you can use Application.Match to test if a value is contained in an array.
Function sshProblem(rng As Range) As Boolean
Dim portStatus As String, sht As WorkSheet
Dim rw as Long
Set sht = rng.WorkSheet
portStatus = rng.Value
Dim deviceType As String
rw = Application.Caller.Row
deviceType = sht.Cells(rw, 3).Value
Dim sshDevices As Variant
sshDevices = Array("linux", "vmw", "docker", "unix")
If StrComp(portStatus, "No") = 0 Then
sshProblem = Not IsError(Application.Match(deviceType, sshDevices, 0))
End If
End Function

VBA Range.Find method not finding a value that IS in the range

I have the following method which finds the largest and smallest values in a range. I am then using those values to locate the actual cell they are in as I need to grab the value from the header cell of that column. The Range.Find is always returning Nothing even though the range being searched HAS A CELL WITH THAT VALUE.
Sub GetTopAndBottomFiveCommodities()
Dim tempRange As Range, x As Integer, bestPnL As Double, worstPnL As Double
Dim strTopRangeName As String, strBottomRangeName As String
Dim cCell As Range, commodityName As String
Set tempRange = dataSourceSheet.Range("A:A").Find(What:="Year Totals")
Set tempRange = Range(tempRange.Offset(0, 1), tempRange.End(xlToRight).Offset(0, -1))
For x = 1 To 5
strTopRangeName = "TopCommodity" & CStr(x)
strBottomRangeName = "BottomCommodity" & CStr(x)
bestPnL = WorksheetFunction.Large(tempRange, x)
worstPnL = WorksheetFunction.Small(tempRange, x)
Debug.Print tempRange.Address
' get the top commodity name and PnL
**Set cCell = tempRange.Find(What:=bestPnL, LookIn:=xlValues)**
commodityName = dataSourceSheet.Cells(5, cCell.Column).Value
Range(strTopRangeName).Value = commodityName
Range(strTopRangeName).Offset(0, 1).Value = bestPnL
Next x
End Sub
The code line
Set cCell = tempRange.Find(What:=bestPnL, LookIn:=xlValues)
is always returning nothing but I have verified that there are cells with that value. One example, the cell value is 66,152.61 (displayed in cell as 66,153) and the bestPnL variable is 66,152.61 , so I tried rounding bestPnL to 66,153, but still didn't find it. The debug statement is showing tempRange has the right range, so its not searching in the wrong place.
The only thing I can think of is the cell with the value, gets its value from a very long formula, using over a dozen named ranges, can this be fouling the find method?
Just so we all know I'm not crazy, here is a snapshot of part of the range I'm searching where I'm testing.
EDIT
Based on Tim Williams suggestion, I changed the number format of the range being searched prior to the Find call.
tempRange.NumberFormat = "0.00"
and then the Find call works as it should. I then just put the number format back the way I want it at the end of the routine.
tempRange.NumberFormat = "$#,##0;[Red]$#,##0"
Works as expected now.
Try removing the thousand separator from the number format on the cells. When I did that in a test range it worked fine, but with the separator it failed to find the value.
Set f = rng.Find(what:=bestPnL, LookIn:=xlFormulas)
will work even with the thousand separator (EDIT: only works with hard-coded values; fails with formulas).
EDIT2: this worked for me with a thousands separator and using formulas for the values (EDIT3!: does not work with currency formatting).
Sub Tester()
Dim f As Range, v, rng As Range
Set rng = Range("C3:C21")
v = Application.Large(rng, 3)
v = Format(v, rng.Cells(1).NumberFormat)
Set f = rng.Find(what:=v, LookIn:=xlValues)
Debug.Print f.Address ' >> C19
End Sub
This is an old question, but I found an alternative that can be effective and simple in some situations:
dim idx as long, rng as range
set rng = someRange
idx = application.WorksheetFunction.Match(1234,rng,0)
This will return the relative position of the FIRST 1234 valued cell in the provided range, independently of the formatting. The last 0 means you use an exact match.

Type Mismatch Run Time error 13 for excel VBA

I have requirement to write some function which will accept Range as input and I need to return value of first non empty cell. I have tried in one excel sheet and finding non empty cell was working fine. When I try with my project excel file it was not working. Basically for Find method of Range I am getting runtime error 13. Check below code and let me know what is the issue. I have noticed even in when I put Range.Row property it make "Row" as row in code ( in below code see Target.row).
Sub Btn_GenerateChartClicked()
If Range("E9") = "Scatter" Then
MsgBox "Scatter is selected"
Dim str As String
Dim rng As Range
Set rng = Range("B12:I12")
str = FindNonEmptyCellFromRange(rng)
' MsgBox str
Else
MsgBox "Bar is selected"
End If
End Sub
Function FindNonEmptyCellFromRange(Target As Range) As String
Dim ws As Worksheet
Set ws = Sheets("Benchmarking_Project")
Dim foundRange As Range
Set foundRange = Target.Find("*", Cells(Target.row, 1), xlFormulas, , xlByColumns, xlPrevious)
'Dim cellValue As String
'cellValue = foundRange.Value
FindNonEmptyCellFromRange = "Test"
'cellValue
End Function
You can't find a target.
use Cell.Find and then once you have the cell selected use Target.Address to get the address of the cell
So your CellValue would become:
CellValue = FoundRange.Address
Although, your question is a little vague as your not doing anything practicle with this UDF anyway
Your question does not provide enough details and the function call does not return the non empty cell. Whatever happens your function will return only Test.
Anyway when going through the code, your range has a single row in it.
Issue seems to be with the following code
Set foundRange = Target.Find("*", Cells(Target.row, 1), xlFormulas, , xlByColumns, xlPrevious)
There is no need to specify the After Parameter Cells(Target.row, 1)
After parameters corresponds to the position of the active cell when a search is done from the user interface. Notice that After must be a single cell in the range. Remember that the search begins after this cell; the specified cell isn’t searched until the method wraps back around to this cell. If you do no specify this argument, the search starts after the cell in the upper-left corner of the range.
Try to change that code to
Set foundRange = Target.Find("*", , xlFormulas, , xlByColumns, xlPrevious)
The following code may work for you
Sub Btn_GenerateChartClicked()
If Range("E9") = "Scatter" Then
MsgBox "Scatter is selected"
Dim str As String
Dim rng As Range
Set rng = Range("B12:I12")
str = GetFirstNonEmptyCell(rng)
' MsgBox str
Else
MsgBox "Bar is selected"
End If
End Sub
Public Function GetFirstNonEmptyCell(Target As Range)
Dim startCell As Range, firstNonEmptyCell As Range
For Each c In Target.Cells
If Trim(c.Value) <> "" Then
Found_Address = c.Address
Exit For
End If
Next
GetFirstNonEmptyCell = Found_Address
End Function
Ian your suggestion about not to use Cells(Target.Row,1) in Find method is right. I got my mistake. In that I have put column index as 1 but it should be 2 because my selected range is from Column B which means column index 2. So I got actually error because there is no column index 1 in that range. So if I put 2 instead of 1 in above mentioned call then it is working fine. Yes your right that I was not returning actually value of last non empty cell as that was my R&D code I kept changing it. So while posting it I forgot to change it. Thank you all for your reply