From sub to Function - excel

The objective is to count the number of comments in a range with a Function (not a sub).
I am trying to convert a Sub into a Function as I would like the range to vary and the user to enter as a formula
I have already searched the internet and got the right function. However for the sake of learning need to know why my Sub works and the Function does not
Function CommentsCounter(myRange3 As Range) As Long
'This one is mine - not working
CommentsCounter = myRange3.SpecialCells(xlCellTypeComments).Count
End Function
Sub working_just_fine()
'This one is working
Dim myRange4 As Range
Set myRange4 = Range("CO1:CO497")
Range("CO505").Value = myRange4.SpecialCells(xlCellTypeComments).Count
End Sub
Its giving the total number of cells and not the number of comments in the range

Interesting question, I tried some wonky stuff like declaring a public variable, then calling a Sub from the Function and putting the result of SpecialCells.Count into the public variable and accessing that from the Function. Surprisingly, calling the Sub from the Function led to a different result than calling the Sub separately. As a solution, this Function should do what you're trying to achieve, although it may be a bit slow on very large ranges, since it goes through every single cell in the range (I haven't done extensive testing on it yet):
Function CommentsCounter(myRange3 As Range) As Long
CommentsCounter = 0
For Each rngCell In myRange3
If Not rngCell.Comment Is Nothing Then CommentsCounter = CommentsCounter + 1
Next
End Function

Related

Using VBA in Excel - Static variable not retained when Multipage object has change event (changing pages)

I am having trouble with losing static variables within an Excel user form.
I have been working on a routine for excel. I am a (very) novice coder.
I am attempting to populate a cell range to an array. I have been able to do this without issue.
However, when I attempt to store the array as a *static * variable, the variable is not retained for as long as I want it to be.
I think the problem occurs when another page is selected in the multipage, the static variable is cleared.
Code is something like this:
Sub UserForm_Initialize ()
static myArray () as variant
dim myRange as range
set myRange = [namedrange]
myArray=myRange
msgbox myArray(0,0) 'this works fine
call OtherSub
end sub
sub OtherSub ()
msgbox myArray(0,0) 'this returns a blank
end sub
The first sub of code shows the array element just fine. The array element is blank in the second sub.
I have tried:
Declaring the array as a public variable, but this returns an error (I think that variables within user forms are private by default and cannot be changed).
using a very small variable (a simple string)
writing code in a module before opening the user form (variable is not retained).
I am aware that I can just write data to a cell range, but this would defeat the purpose. I was hoping to avoid multiple instances of reading large arrays from the worksheet.
This might explain it a bit clearer. Moving MyArray outside of the Procedure will set it's scope to a Module Level, making it usable through other subs within that module. You will generally want to keep the scope of your variables to the lowest level required. The other option would be to pass your variable as a parameter to your other procedure.
Option Explicit
Dim MyArray() As Variant ' Private Module Level Scope
Public ExampleVariable As String ' Public Module Level Scope (Not required, just showing an example.)
Sub UserForm_Initialize()
Dim myRange As Range ' Procedure Level Scope
Set myRange = [namedrange]
MyArray = myRange
MsgBox MyArray(0, 0) 'this works fine
Call OtherSub
End Sub
Sub OtherSub()
MsgBox MyArray(0, 0) 'this returns a blank
End Sub

Assigning excel function to a variable in Excel VBA

I am trying to assign am excel function to a VBA variable. I have tried a few different data types and none of them seem to work. I am trying to count the columns and rows in this table "tblAdsorpsionColumn" and I dont want to type out "Application.WorksheetFunction" every time I use an excel function in VBA. I know there other ways to do this but I want to use this way to learn how to use this functionality later. (what doesn't work: Worksheet,Double,String,Object)
Private Sub UserForm_Initialize()
Dim App As Object
Dim Table As Range
Set App = Application.WorksheetFunction
Set Table = ThisWorkbook.Worksheets("Sheet1").Range("tblAdsorpsionColumn")
lstTable.ColumnCount = App.Columns(Table)
End Sub
Say you use the SUM() worksheet function frequently in VBA and don't want to repeat typing Application.WorksheetFunction constantly. Just make a VBA wrapper like:
Option Explicit
Public Function summ(rng As Range) As Variant
With Application.WorksheetFunction
summ = .Sum(rng)
End With
End Function
You will get a very small performance hit using it, but it may be tolerable.
You can use a function to "shortcut" Application.WorksheetFunction :
Sub tester()
Debug.Print wsf.Average(Selection), wsf.Sum(Selection)
End Sub
Function wsf() As Object
Set wsf = Application.WorksheetFunction
End Function

Error 91 in a function but not in a similar sub

I want to convert a string into a range variable that I will use somewhere else. My first idea was to create a text-to-range function and to avoid dealing with the sintaxis ever again. I know it is probably a very basic question but I couldn't figure it.
My first attempt was this. It prints "indirect" were I want to (this was just a test), but running the macro step by step I see that there is an error 91 at the time to "exit" the function.
Dim rng As Range
rng = TXT2RNG(Range("A1").Value)
'This is the function, located in Modulo1
'Function TXT2RNG(text As String) As Range
'Set TXT2RNG = Range(text)
'TXT2RNG.Value = "indirect"
'End Function
End Sub
I have attempted the same but without the function, and it works as I expected.
Dim rng As Range
Set rng = Range(Range("A2").Value)
'Set rng = Range(Range("A1").Value)
rng.Value = "direct"
End Sub
Summary: The second code works as a workaround but I want to know why the first one doesn't so can learn from it and use similar structures in the future. Thank you
Basically, you are simply missing a Set when assigning the result from the function to your variable - you do it correct in your direct example.
Whenever you are dealing with objects (eg worksheet, range), you need to use Set when assigning it to a variable. A good explanation can be found at https://stackoverflow.com/a/18928737/7599798
Omitting the Set will cause an error 91 when assigning it to an object variable. However, if you would declare your rng-variable as Variant, you wouldn't get a runtime error. Instead, VBA would use the so called default property, for a Range this is the Value, so you would end up having the content of the Range in your variable ("indirect" in your example). This is the reason to use the data type Variant only if really needed.
That said, there are at least 2 issues you should take care about:
when you use the Range-function as you do, it refers to ActiveSheet, which is the sheet that currently has the focus. When coding, that's not always what you want, so think about if you need to approve your function. You should really take the time to read the answers of How to avoid using Select in Excel VBA to get an understanding.
You should think about what should happen when the text you pass to your function doesn't contain a valid range-address. Currently, you would get a runtime error (1004). Error handling in VBA is done with On Error-statements. You should avoid On Error Resume Next.
You could change your function to:
Function TXT2RNG(ws as Worksheet, text As String) As Range
On Error Goto InvalidRange
Set TXT2RNG = ws.Range(text)
' TXT2RNG.Value = "indirect"
Exit Function
InvalidRange:
' Think about what to do here, show a message, simply ignore it...
Set TXT2RNG = Nothing
End Function
And the call to it would be
Dim rng as Range, address as string
address = Range("A1").Value
Set rng = TXT2RNG(activeSheet, address)
if not rng is Nothing then
(...)
Welcome to stack overflow.
From walking through your code, you're not declaring "text" as anything.
If you use the "Watches" feature, you can see it's a blank string
I believe you need to have a function that pulls the range, then a second function to pull the string of that. A Private Sub is much better
See this answer https://stackoverflow.com/a/2913690/2463166

Run time Error 1004 when calling function inside of a For Each loop

I have a function that finds any bold words within a specific range. This function is called in the for each loop below. The way it's currently set up it returns true if bold words exist anywhere in the range, or false if no bold words exist in the range. What I want to do is get it so it stops when it finds the bold words so I can do some other actions, then continue on down the list. I was thinking of doing something along these lines, however it throws an error on the second line
dim i as range
set i = range("C11:C6000")
for each i in range("C11:C6000")
FindBoldCharacters(Range(i)) 'throws "run time error 1004 application or object defined error" here
if i = true then
'do some stuff here'
else
end if
next i
here is the function that the piece of code is calling
Function FindBoldCharacters(ByVal aCell As Range) As Boolean
FindBoldCharacters = IsNull(aCell.Font.Bold)
If Not FindBoldCharacters Then FindBoldCharacters = aCell.Font.Bold
End Function
The function works when just called by itself, but doesn't work when I try to call it in this way. What can I do to solve this problem?
As mentioned in my comment there are a couple of things off with your code as is.
The variable i, if undeclared prior to this loop, will be dimensioned at run time as a Range object, not as a string holding the cell address as you appear to be using it as
Related, i is later being used as a Boolean when it is a Range object
Each For.. should only have one Next, your code will throw a Next without For error if you try to run it
First, declare i as a Range object, and then pass it into your function like so FindBoldCharacters(i) (this makes sense as in your function declaration you say you are expecting aCell as a Range)
Second, evaluate the function directly in the If statement
Finally, move the Next i from inside the If statement outside.
Something like this:
Option Explicit
Sub Test()
Dim i As Range
For Each i In Range("C11:C6000")
If FindBoldCharacters(i) = True Then
'do some stuff here'
Else
End If
Next i
End Sub

Passing a variable defined in a sub onto a function VBA

I´m trying to use a sub to determine a dynamic range which serves as the input to a number of functions. The simplest version of what I´m trying to do looks like this
This approach gives me errors. Putting the subs and functions in different modules doesn´t help. What´s the mistake I´m making?
Global Info As Range
Sub InfoSetter()
Worksheets("Example").Activate
ActiveSheet.UsedRange.Select
Set Info = Selection
End Sub
Function Test() As Variant
Test = Info.Address
End Function
Try the below. This calls the sub from the function that sets the value of info. This means that your info will always be the current UsedRange when the function calculates. Also, using .Activate and .Select is generally considered bad practice. The below code does the same without using either.
Global Info As Range
Sub InfoSetter()
Set Info = Worksheets("Example").UsedRange
End Sub
Function Test() As Variant
Application.Volatile 'Added so function updates automatically when sheet calculates
Call InfoSetter
Test = Info.Address
End Function
Edit: Adding Application.Volatile at the beginning of your function will make it so this updates every time the worksheet calculates.
As per the comment from omegastripes, your error is likely to be because your range is empty. Given Test is a variant suggest you test for an empty range as below
Global Info As Range
Sub TryMe()
MsgBox Test
End Sub
your code
Sub InfoSetter()
Set Info = Worksheets("Example").UsedRange
End Sub
Function Test() As Variant
If Info Is Nothing Then
Test = "Ï'm empty"
Else
Test = Info.Address
End If
End Function

Resources