VBA - UDF result not showing correctly [duplicate] - excel

I am trying to write a UDF that counts the number of cells that have conditional formatting. I wrote the following sub that works like a charm:
Sub SumCountByConditionalFormat()
Dim cellrngi As Range
Dim cntresi As Long
cntresi = 0
Set cellrngi = Sheets("Sheet3").Range("I2:I81")
For Each i In cellrngi
If i.DisplayFormat.Interior.Color <> 16777215 Then
cntresi = cntresi + 1
End If
Next i
end sub
and I tried to convert it to a UDF with the following code:
Function CountCellsByColor1(rData As Range) As Long
Dim cntRes As Long
Application.Volatile
cntRes = 0
For Each cell In rData
If cell.DisplayFormat.Interior.Color <> 16777215 Then
cntRes = cntRes + 1
End If
Next cell
CountCellsByColor1 = cntRes
End Function
However when I try the UDF i get a #VALUE! returned. I'm really not sure why and any help would be much appreciated.

You can work around the inability to access DisplayFormat in a UDF using Evaluate
Function DFColor(c As Range)
DFColor = c.DisplayFormat.Interior.Color
End Function
Function CountCellsByColor1(rData As Range) As Long
Dim cntRes As Long, clr As Long, cell As Range
cntRes = 0
For Each cell In rData.Cells
'Evaluate the formula string in the context of the
' worksheet hosting rData
clr = rData.Parent.Evaluate("DFColor(" & cell.Address() & ")")
If clr <> 16777215 Then
cntRes = cntRes + 1
End If
Next cell
CountCellsByColor1 = cntRes
End Function

Related

Problems with conditional formating and counting of colored cells after that [duplicate]

I am trying to write a UDF that counts the number of cells that have conditional formatting. I wrote the following sub that works like a charm:
Sub SumCountByConditionalFormat()
Dim cellrngi As Range
Dim cntresi As Long
cntresi = 0
Set cellrngi = Sheets("Sheet3").Range("I2:I81")
For Each i In cellrngi
If i.DisplayFormat.Interior.Color <> 16777215 Then
cntresi = cntresi + 1
End If
Next i
end sub
and I tried to convert it to a UDF with the following code:
Function CountCellsByColor1(rData As Range) As Long
Dim cntRes As Long
Application.Volatile
cntRes = 0
For Each cell In rData
If cell.DisplayFormat.Interior.Color <> 16777215 Then
cntRes = cntRes + 1
End If
Next cell
CountCellsByColor1 = cntRes
End Function
However when I try the UDF i get a #VALUE! returned. I'm really not sure why and any help would be much appreciated.
You can work around the inability to access DisplayFormat in a UDF using Evaluate
Function DFColor(c As Range)
DFColor = c.DisplayFormat.Interior.Color
End Function
Function CountCellsByColor1(rData As Range) As Long
Dim cntRes As Long, clr As Long, cell As Range
cntRes = 0
For Each cell In rData.Cells
'Evaluate the formula string in the context of the
' worksheet hosting rData
clr = rData.Parent.Evaluate("DFColor(" & cell.Address() & ")")
If clr <> 16777215 Then
cntRes = cntRes + 1
End If
Next cell
CountCellsByColor1 = cntRes
End Function

Select random cell in range

I'm trying to perform an action in VBA on a range of cells. I would like the selection of the cells to be random not in the order of how the range is setup.
Sub Solver_Step_Evo()
Set Rng = GetRange(ThisWorkbook.ActiveSheet.Range("Variable_Range"))
For Each i In Rng
'perform an action on I where I is randomly selected.
Next i
End Sub
My preference is it randomizes the order not just randomly select a cell where a cell can be picked more than once.
Thanks in advance.
Here's a possible solution. I add all of the cells in the relevant range to a collection. Then, I navigate the collection using random indexes. Once an index has been visited, I remove it from the collection and repeat the process.
Does this work for you?
Edit: No need to call the c.Count method for each iteration. We can manage this ourselves ourselves. It would likely be a bit more efficient than calling the object's method.
Sub SuperTester()
Dim c As Collection
Dim rng As Range
Dim cel As Range
Dim idx As Long
Dim remainingCount As Long
Set rng = Range("A2:A17")
Set c = New Collection
For Each cel In rng
c.Add cel
Next cel
remainingCount = c.Count
While remainingCount > 0
idx = WorksheetFunction.RandBetween(1, c.Count)
Debug.Print c.Item(idx).Address
c.Remove idx
remainingCount = remainingCount - 1
Wend
End Sub
You can use WorksheetFunction.RandBetween to get random number between 2 numbers. The numbers will not be unique though. If you want unique then you will have to use a slightly different approach.
Option Explicit
Sub Solver_Step_Evo()
Dim Rng As Range
Set Rng = GetRange(ThisWorkbook.ActiveSheet.Range("Variable_Range"))
Dim lowerBound As Long: lowerBound = 1
Dim UpperBound As Long: UpperBound = Rng.Cells.Count
Dim randomI As Long
Dim i As Long
For i = lowerBound To UpperBound
randomI = Application.WorksheetFunction.RandBetween(lowerBound, UpperBound)
Debug.Print randomI
Next i
End Sub
Try the next function, please:
Function RndCell(rng As Range) As Range
Dim rndRow As Long, rndCol As Long
rndRow = WorksheetFunction.RandBetween(1, rng.rows.count)
rndCol = WorksheetFunction.RandBetween(1, rng.Columns.count)
Set RndCell = rng.cells(rndRow, rndCol)
End Function
It can be tested using the next simple sub:
Sub testSelectRandomCell()
Dim rng As Range
Set rng = Range("A2:D10")
RndCell(rng).Select
End Sub
Edited:
If the random selected cells should not repeat, the function can be adapted in the next way (using a Static array to keep the already selected cells):
Function RndCellOnce(rng As Range, Optional boolClear As Boolean = False) As Range
Dim rndRow As Long, rndCol As Long, k As Long, El, arr1
Static arr
If boolClear And IsArray(arr) Then Erase arr
DoItAgain:
rndRow = WorksheetFunction.RandBetween(1, rng.rows.count)
rndCol = WorksheetFunction.RandBetween(1, rng.Columns.count)
If IsArray(arr) Then
If UBound(arr) = rng.cells.count - 1 Then
rng.Interior.Color = xlNone
ReDim arr(0): GoTo Over
End If
For Each El In arr
If El <> "" Then
arr1 = Split(El, "|")
If CLng(arr1(0)) = rndRow And CLng(arr1(1)) = rndCol Then GoTo DoItAgain
End If
Next El
ReDim Preserve arr(UBound(arr) + 1)
Else
ReDim arr(0)
End If
Over:
arr(UBound(arr)) = rndRow & "|" & rndCol
Set RndCellOnce = rng.cells(rndRow, rndCol)
End Function
It can be tested with the next Sub. In order to visually check it, each selected cell will get a yellow interior color. When all the range cells will be selected (one by one), the static array will be erased and the interior color will be cleaned:
Sub testSelectRandomCell()
Dim rng As Range
Set rng = Range("A2:D10")
With RndCellOnce(rng)
.Interior.Color = vbYellow
.Select
End With
End Sub

Count number of colored cells in range using VBA

I have a code for counting number of colored cell. Can you please advice how to extend range?
Basically I need to count colored cells columns range like ("A:Z") and nRowIndex = 2 To 100?
Sub CountCellsWithBackgroundColor()
Dim nRowIndex As Integer, nCellNumber As Integer
'Go through the range
Worksheets("Report_Rule_S").Activate
For nRowIndex = 2 To 100
If Range("E" & nRowIndex).Interior.ColorIndex <> -4142 Then 'need to do somthing like Range("A:Z")
nCellNumber = nCellNumber + 1
End If
Next nRowIndex
' Output the result
Worksheets("cover").Range("B12") = nCellNumber
End Sub
You can put all of this in one single For-Each loop:
Dim aCell As Range
Dim cellNumber As Integer
cellNumber = 0
For Each aCell In Range("A2:Z100").Cells:
If aCell.Interior.ColorIndex <> -4142 Then
cellNumber = cellNumber + 1
End If
Next
my comments below
Sub CountCellsWithBackgroundColor()
Dim nRowIndex As Integer, nCellNumber As Integer
'Go through the range
Worksheets("Report_Rule_S").Activate
For Each cl In Worksheets("Report_Rule_S").UsedRange.SpecialCells(xlCellTypeVisible).Cells
If cl.Interior.ColorIndex <> -4142 Then
nCellNumber = nCellNumber + 1
End If
Next cl
'Output the result
Worksheets("cover").Range("B12") = nCellNumber
End Sub

Select Random Cell In A Range Only If It Has A Value - Excel

So here is the following VBA code I'm currently using. It works perfectly but I need to expand the range to check additional cells but some of those cells could contain empty cells and I don't want to select those.
Is there a way to bypass those empty cells?
Dim RNG1 As Range
Set RNG1 = Range("H1:H30")
Dim randomCell1 As Long
randomCell1 = Int(Rnd * RNG1.Cells.Count) + 1
With RNG1.Cells(randomCell1)
.Select
'will do something else here, like copy the cell, fill the cell with a color, etc
End With
This should pick only non-empty cells:
Sub marine()
Dim RNG1 As Range, r As Range, c As Collection
Set c = New Collection
Set RNG1 = Range("H1:H30")
For Each r In RNG1
If r.Value <> "" Then
c.Add r
End If
Next r
Dim N As Long
N = Application.WorksheetFunction.RandBetween(1, c.Count)
Set rselect = c.Item(N)
rselect.Select
End Sub
NOTE:
This is an example of a general technique. To make a random pick from a subset of a range, collect the subset and pick from the Collection.
If the values in column H were XlConstants then something like this using SpecialCells
Sub Option_B()
Dim rng1 As Range
Dim rng2 As Range
Dim lngCel As Long
On Error Resume Next
Set rng1 = Range("H1:H30").SpecialCells(xlCellTypeConstants)
On Error GoTo 0
If rng1 Is Nothing Then Exit Sub
Dim randomCell1 As Long
randomCell1 = Int(Rnd * rng1.Cells.Count) + 1
For Each rng2 In rng1.Cells
'kludgy as there will be multiple areas in a SpecialFCells range with blank cells
lngCel = lngCel + 1
If lngCel = randomCell1 Then
Application.Goto rng2
Exit For
End If
Next
End Sub
A bit too late but no harm in posting :)
Sub test()
Dim rng As Range, cel As Range
Dim NErng
Dim i As Integer
Set rng = Range("A1:A15")
For Each cel In rng
If Len(cel) <> 0 Then
If IsArray(NErng) Then
ReDim Preserve NErng(UBound(NErng) + 1)
NErng(UBound(NErng)) = cel.Address
ElseIf IsEmpty(NErng) Then
NErng = cel.Address
Else
NErng = Array(NErng, cel.Address)
End If
End If
Next
i = Int((UBound(NErng) - LBound(NErng) + 1) * Rnd + LBound(NErng))
Debug.Print Range(NErng(i)).Address
End Sub
EDIT -- #brettdj is right. This is adjusted to better answer the "skip these cells" question.
Try this out:
DangThisCellIsBlank:
RandomCell = Int(Rnd * RNG1.Cells.Count) + 1
With RNG1.Cells(RandomCell)
If .Value <> "" Then
'do stuff
Else
'go back and pick another cell
GoTo DangThisCellIsBlank
End If
End With
Try with IsEmpty(RNG1.Cells(randomCell1))
Dim RNG1 As Range
Set RNG1 = Range("H1:H30")
Dim randomCell1 As Long
randomCell1 = Int(Rnd * RNG1.Cells.Count) + 1
'Keep Looping until you find a non empty cell
Do While IsEmpty(RNG1.Cells(randomCell1))
randomCell1 = Int(Rnd * RNG1.Cells.Count) + 1
Loop
'================================================
With RNG1.Cells(randomCell1)
.Select
'will do something else here, like copy the cell, fill the cell with a color, etc
End With

VBA: How to ignore hidden rows in range?

I am trying to do a count of all rows that are highlighted and are visible (not hidden). My count formula works but it is still counting hidden rows that also happen to be hidden. How can I count only highlighted and visible rows?
'This function will count how many cells in a given range for a given color and are visible
Function COUNTCELLCOLORSIF(CellRange As Range) As Long
Dim rngCell
Application.Volatile
For Each rngCell In CellRange
If rngCell.Interior.ColorIndex = "36" and rngCell.visible Then
COUNTCELLCOLORSIF = COUNTCELLCOLORSIF + 1
End If
Next rngCell
End Function
Use specialcells(xlcelltypevisible)
Function COUNTCELLCOLORSIF(CellRange As Range) As Long
Dim rngCell
Application.Volatile
For Each rngCell In CellRange.specialcells(xlcelltypevisible)
If rngCell.Interior.ColorIndex = "36" Then
COUNTCELLCOLORSIF = COUNTCELLCOLORSIF + 1
End If
Next rngCell
End Function
Try something like this:
Function COUNTCELLCOLORSIF(CellRange As Range) As Long
Dim rngCell, visibleCells
Application.Volatile
visibleCells = CellRange.SpecialCells(xlCellTypeVisible)
For Each rngCell In visibleCells
If rngCell.Interior.ColorIndex = "36" and rngCell.visible Then
COUNTCELLCOLORSIF = COUNTCELLCOLORSIF + 1
End If
Next rngCell
End Function

Resources