Find and FindNext for Excel VBA - vba

I've been stuck trying to figure out what to do with this, but basically I want a way to print out the value in column B given a specific value that matches column A. So for example:
Column A Column B
1 ABC
2 DEF
3 GHI
1 JKL
I want to, after using find/findnext or whatever it is, to print out this string:
ABC JKL
I tried using
Set cellFound = ActiveWorkbook.Worksheets("sheet1").Range("F1:F1000000").Find("1")
string = cellFound.Offset(0, 1).value
And I have a loop to loop through as many time as it needs to get all the rows taken care of. But with find it keeps returning me the same first string ("ABC") and the string ends up being ABC ABC instead of ABC JKL
I tried using FindNext instead of find, but what I got is a 1004 Error. So I'm not really sure where I'm doing this wrong. Anyone has any idea?

You don't need FindNext if you start each Find after the previous one:
Sub qwerty()
Dim rFirst As Range, r As Range
Dim A As Range
Set A = Range("A:A")
Do
If rFirst Is Nothing Then
Set rFirst = A.Find(What:=1, After:=A(1))
Set r = rFirst
Else
Set r = A.Find(What:=1, After:=r)
If r.Address = rFirst.Address Then Exit Do
End If
MyString = MyString & " " & r.Offset(0, 1)
Loop
MsgBox MyString
End Sub

You need to call Find once, and then successively FindNext. But there are a couple of non-obvious things:
Each time you call FindNext, the search will start again from the upper-left corner of the range; unless you pass in the current found cell.
The search will wrap around (up or down, depending on your search direction. You need to write code that handles this possibility.
The minimal working code would look something like this:
Dim rng As Excel.Range
Set rng = ActiveWorkbook.Worksheets("sheet1").Range("F1:F1000000")
Dim lastRow as Integer
Set cellFound = rng.Find("1")
Do While Not cellFound Is Nothing
' handles wraparound
If cellFound.Row < lastRow Then Exit Do
string = cellFound.Offset(0, 1).Value
' do something here with string
Set cellFound = rng.FindNext(cellFound)
Loop
Reference:
Find method
FindNext method

When using the Range.FindNext method, one need just include some reference to the initial find position. For example, I recorded this macro using excel; while I'm not a fan of using selection and activate, I think it helps to understand how the method functions:
Sub Using_Find()
Selection.Find(What:="my search string here", After:=ActiveCell _
, LookIn:=xlFormulas, LookAt:=xlPart, SearchOrder:=xlByRows, _
SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False).Activate
Selection.FindNext(After:=ActiveCell).Activate
Selection.FindNext(After:=ActiveCell).Activate
Selection.FindNext(After:=ActiveCell).Activate
Selection.FindNext(After:=ActiveCell).Activate
Selection.FindNext(After:=ActiveCell).Activate
Selection.FindNext(After:=ActiveCell).Activate
End Sub
To generate this subroutine, I used the record > macro in excel, then selected Home > Find & Select > Find.
The way I see this subroutine working is:
Step #1: Find the first location of the string, activate it;
Step #2: FindNext looks after the active cell that we just activated, finds the next location of the string, then activates it;
Etc. etc. So, the observation here is that the .FindNext method needs some reference to the prior find cell (which the first answer accomplishes by manually identifying it as a unique reference). I'm not saying anything to that answer, it works just as well. My goal was to help provide some insight into the Range.FindNext method.
Some other points worth mentioning:
Range.FindNext will return a Range object. (Microsoft)
The After parameter is described as:
"The cell after which you want to search. This corresponds to the position of the active cell when a search is done from the user interface. Be aware that After must be a single cell in the range. Remember that the search begins after this cell; the specified cell is not searched until the method wraps back around to this cell. If this argument is not specified, the search starts after the cell in the upper-left corner of the range." (Microsoft)
...and
Under the Remarks section, Microsoft notes that, "The search will wrap around to the beginning of the range." They suggest to save the first address and do a check against it for each subsequent .FindNext. This way, once the method does wrap around, it will check the address against the first and end the check.
So, modeling the Range.FindNext Method provided by Microsoft, I wrote this introductory subroutine for review:
Sub USING_FIND()
'this line sets the range to our used range on the active sheet
With ActiveSheet.UsedRange
'setting c variable to .Find method, where the first value is what we're looking for,
'i.e. "1"; LookIn:= can be changed to our needs but set currently to xlValues
Set c = .Find(1, LookIn:=xlValues)
'begin first conditional; this conditional checks c (our .Find method) to see if it has
'some reference, then sets the address to a constant 'firstAddress' so we can check it
'against the .FindNext returns later to prevent endless loop
If Not c Is Nothing Then
firstAddress = c.Address
'Do...is where we place our "work"; this can be a redirect to another function/sub, etc
'for now I've just tossed a msgbox as a placeholder that returns the offset 1 column over
Do
MsgBox c.Offset(0, 1)
'Now we set c to the .FindNext method, using the original .Find method as the 'after'
Set c = .FindNext(c)
'Another empty reference check/exit as a conditional
If c Is Nothing Then
GoTo DoneFinding
'ends the empty reference conditional
End If
'using our .FindNext method that we replaced 'c' with earlier, we can now loop through
'the remainder of the value returns. The Loop While 'c.Address <> firstAddress' sentence
'is checking that each subsequent .FindNext address IS NOT the first address;
'-our loop will return to the 'Do' sentence to repeat the loop, starting on the
'MsgBox c.Offset(0,1) sentence with the next string occurence
'-the characters '<>' means 'does not equal'; i.e. the opposite of '='
Loop While c.Address <> firstAddress
'this ends the address check loop
End If
DoneFinding:
End With
End Sub
To adjust this code to your specific needs, we can change the sentence after the Do line: 'MsgBox c.Offset(0,1)' to our specific needs.
Depending on how complex your output needs are, you can add all occurrences to an array, then have the array output the values in order of how you want to see them. This can be done by redim array and preserve each return. Once the .Find loop completes, open a new workbook with the Workbooks.Open method, and run a quick loop that takes each array value and places it in the order that you prefer.
Another option is to 'print' to .txt. Open a new .txt as #1, then 'print' accordingly. This can also be done as a second subroutine via the array option suggested previously.
Hope this helps add some context to your initial question with respect to the .FindNext method, as well as provides some ideas for future direction/implementation. Good luck!
Microsoft page on Range.FindNext Method:
https://msdn.microsoft.com/en-us/VBA/Excel-VBA/articles/range-findnext-method-excel

Function FindMultiResut(ByRef What As String, _
ByRef FindRng As Range, _
ByRef OutputRng As Range, _
ByRef Delimite As String)
Dim fRng As Range
Dim Rng1 As Range
Dim Rng2 As Range
Dim temp As String
Set fRng = FindRng
Do
If Rng1 Is Nothing Then
Set Rng1 = fRng.Find(What:=What, After:=fRng(1))
Set Rng2 = Rng1
Else
Set Rng2 = fRng.Find(What:=What, After:=Rng2)
If Rng2.Address = Rng1.Address Then Exit Do
End If
If OutputRng.Worksheet.Cells(Rng2.Row, OutputRng.Column) <> Empty Then
temp = temp & OutputRng.Worksheet.Cells(Rng2.Row, OutputRng.Column) & Delimite
End If
Loop
FindMultiResut = Left(temp, Len(temp) - 1)
End Function

Here is an implementation of the suggestion I made in my comment under your question.
Function RowBeforeLast(ByVal What As Variant) As Long
Dim Fnd As Range
Set Fnd = Range("E:E").Find(What:=What, After:=Range("E1"), _
LookAt:=xlWhole, _
Searchdirection:=xlPrevious)
If Not Fnd Is Nothing Then
Set Fnd = Range("E:E").Find(What:=What, After:=Fnd, _
LookAt:=xlWhole, _
Searchdirection:=xlPrevious)
If Not Fnd Is Nothing Then RowBeforeLast = Fnd.Row
End If
End Function
It's designed as a UDF so that you can call it from the worksheet with a worksheet function like =RowBeforeLast(E5). You can also call it with code like
Private Sub TestGet()
RowBeforeLast "GR 3"
End Sub
Either way it will return the row number in which the search criterium was found for the second time from the bottom of the column. If there is only one or no occurrance the function will return zero.

Related

How can I create a custom function using a variable-sized array input?

I am very new to advanced Excel use, so apologies in advance for anything I mis-identify.
In trying to automate some statistics gathering on a spreadsheet, I want to be able to define a custom function (either through a name definition or a VBA function) that can calculate a value with an array of indeterminate length.
Currently, the function I can copy-paste and change cell values is as follows:
=SUMPRODUCT((I46:I58="D")*(L46:L58<>"Anonymous"))+
SUMPRODUCT((I46:I58="Throwaway")*(L46:L58="Anonymous"))
Amending the array for every single use of the function is not optimal for the amount of data that I will be processing, and I'd like to find some way to have a name definition that automatically populates the arrays (perhaps by searching the I column for a specific value) or VBA function that populates the row numbers based on input cells.
I have tried a few different solutions based on searching for various functions, but I haven't been able to come up with a winning combination. Is there a better way to programatically populate the array values when the length is different for each function run?
Something like the following. You added additional information which is incomplete for range determination. You can use Range.Find method to get the cell where "Goal" is found and take the row number of that cell to determine the row end of your range. What is unknown is which column that is in and also how you are determining the "row of output" for your range start.
With that information you would use that range address in the function below.
Option Explicit
Public Sub test()
Debug.Print Foo(Range("L46:L58"))
Debug.Print Foo(Range("L46:L90"))
End Sub
Public Function Foo(ByVal rng As Range) As Double
rng.Parent.Evaluate ("SUMPRODUCT((" & rng.Address & "=""D"")*(" & rng.Address & "<>""Anonymous""))+SUMPRODUCT((" & rng.Address & "=""Throwaway"")*(" & rng.Address & "=""Anonymous""))")
End Function
Still not really sure what you're looking for, but this would be my best guess:
Function Counts()
Application.Volatile
Const FORMULA As String = _
"SUMPRODUCT(1*(I<r1>:I<r2>=""D""),1*(L<r1>:L<r2><>""Anonymous""))+" & _
"SUMPRODUCT(1*(I<r1>:I<r2>=""Throwaway""),1*(L<r1>:L<r2>=""Anonymous""))"
Dim clr As Range, f As Range, ws As Worksheet, rv, txt
Set clr = Application.ThisCell '<< this is the cell holding the formula
Set ws = clr.Parent '<< the worksheet
'find the first "Goal" in ColI
Set f = ws.Range(ws.Cells(clr.Row, "I"), _
ws.Cells(ws.Rows.Count, "I").End(xlUp)).Find(what:="Goal", lookat:=xlWhole)
If Not f Is Nothing Then
txt = Replace(FORMULA, "<r1>", clr.Row)
txt = Replace(txt, "<r2>", f.Row)
'Debug.Print txt
rv = ws.Evaluate(txt)
Else
rv = "No Goal!"
End If
Counts = rv
End Function
Looks like this:
Where the yellow-shaded cell has =COUNTS()

Select cells between bold cells using a loop

I am working with data where the only consistency is the layout and the bold headings to distinguish between a new date.
I am trying to find the cells in between these cells in bold, find the value "Individual" (in column A) in the selected rows, then sum the values of the given rows in column D (as there can be more then 1 row with "Individual"), and copy this new value to a different cell.
Since the cells between the bold is one date, if the value is not there, the output cell needs to shift down one without filling in anything.
Here is what I have so far:
Sub SelectBetween()
Dim findrow As Long, findrow2 As Long
findrow = range("A:A").Find("test1", range("A1")).Row
findrow2 = range("A:A").Find("test2", range("A" & findrow)).Row
range("A" & findrow + 1 & ":A" & findrow2 - 1).Select
Selection.Find("Individual").Activate
range("D" & (ActiveCell.Row)).Select
Selection.copy
sheets("Mix of Business").Select
range("C4").Select
ActiveSheet.Paste
Exit Sub
errhandler:
MsgBox "No Cells containing specified text found"
End Sub
How can I loop through the data and each time it loops through a range, no matter if it finds the value (e.g. individual) or not, shifts down one row on the output cell? Also, how can I change the findrow to be a format (Bold) rather then a value?
Here is some data for reference:
This is what I am trying to get it to look like:
So you have a good start to trying to work through your data. I have a few tips to share that can hopefully help get you closer. (And please come back and ask more questions as you work through it!)
First and foremost, try to avoid using Select or Activate in your code. When you look at a recorded macro, I know that's all you see. BUT that is a recording of your keystrokes and mouseclicks (selecting and activating). You can access the data in a cell or a range without it (see my example below).
In order to approach your data, your first issue is to figure out where your data set starts (which row) and where it ends. Generally, your data is between cells with BOLD data. The exception is the last data set, which just has a many blank rows (until the end of the column). So I've created a function that starts at a given row and checks each row below it to find either a BOLD cell or the end of the data.
Private Function EndRowOfDataSet(ByRef ws As Worksheet, _
ByVal startRow As Long, _
Optional maxRowsInDataSet As Long = 50) As Long
'--- checks each row below the starting row for either a BOLD cell
' or, if no BOLD cells are detected, returns the last row of data
Dim checkCell As Range
Set checkCell = ws.Cells(startRow, 1) 'assumes column "A"
Dim i As Long
For i = startRow To maxRowsInDataSet
If ws.Cells(startRow, 1).Font.Bold Then
EndRowOfDataSet = i - 1
Exit Function
End If
Next i
'--- if we make it here, we haven't found a BOLD cell, so
' find the last row of data
EndRowOfDataSet = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
End Function
To show you how to use that with your specific data, I've created a test subroutine indicating how to loop through all the different data sets:
Option Explicit
Public Sub DataBetween()
Dim thisWB As Workbook
Dim dataWS As Worksheet
Set thisWB = ThisWorkbook
Set dataWS = thisWB.Sheets("YourNameOfSheetWithData")
'--- find the first bold cell...
'Dim nextBoldCell As Range
'Set nextBoldCell = FindNextBoldInColumn(dataWS.Range("A1"))
'--- now note the start of the data and find the next bold cell
Dim startOfDataRow As Long
Dim endOfDataRow As Long
Dim lastRowOfAllData As Long
startOfDataRow = 3
lastRowOfAllData = dataWS.Cells(ws.Rows.Count, "A").End(xlUp).Row
'--- this loop is for all the data sets...
Loop
endOfDataRow = EndRowOfDataSet(dataWS, startOfDataRow)
'--- this loop is to work through one data set
For i = startOfDataRow To endOfDataRow
'--- work through each of the data rows and copy your
' data over to the other sheet here
Next i
startOfDataRow = endOfDataRow + 1
Do While endOfDataRow < lastRowOfAllData
End Sub
Use both of those together and see if that can get you closer to a full solution.
EDIT: I should have deleted that section of code. It was from an earlier concept I had that didn't completely work. I commented out those lines (for the sake of later clarity in reading the comments). Below, I'll include the function and why it didn't completely work for this situation.
So here's the function in question:
Public Function FindNextBoldInColumn(ByRef startCell As Range, _
Optional columnNumber As Long = 1) As Range
'--- beginning at the startCell row, this function check each
' lower row in the same column and stops when it encounters
' a BOLD font setting
Dim checkCell As Range
Set checkCell = startCell
Do While Not checkCell.Font.Bold
Set checkCell = checkCell.Offset(1, 0)
If checkCell.Row = checkCell.Parent.Rows.Count Then
'--- we've reached the end of the column, so
' return nothing
Set FindNextBoldInColumn = Nothing
Exit Function
End If
Loop
Set FindNextBoldInColumn = checkCell
End Function
Now, while this function works perfectly well, the situation is DOES NOT account for is the end of the last data set. In other words, a situation like this:
The function FindNextBoldInColumn will return nothing in this case and not the end of the data. So I (should have completely) deleted that function and replaced it with EndRowOfDataSet which does exactly what you need. Sorry about that.

How to store a cell address to variable in VBA to continually change Range limits

I am trying to write a macro that searches a whole sheet for a value and then stores the values and locations of ALL hits for that value throughout the sheet. I will do something with the locations and values later, but I need to get this bit working first.
Originally, I used the Range.Find function with iteration and noticed that I was returning the same value. I then tried to have the range being searched change each time a value was found. I would take the address of the previously found value and make it the lower bound of the range.
This worked, to a point, but I ended up getting an infinite loop at the end. The end condition for my loop was when the Range.Find found nothing (since the size of the sheet is always changing and I don't know what the real upper limits will be). What happened was the Range.Find would get stuck on the last value and refuse to move from that spot, regardless of the change I made in the range.
My most recent attempt to deal with this was to also change the After:= input to see if that would force the program to move on. It ended up wrecking the process I already had and now I get stuck in an infinite loop with the first value. So, naturally, I just took that part out hoping to make it work again. No luck.
Here's the code:
[code]
Sub SearchLibrary()
'
' SearchLibrary Macro
' Searches MC library for inputed value and returns all related inforamtion
in Search sheet
'
' Keyboard Shortcut: Ctrl+s
'
'Search code to find all matching values and corresponding headers
' Define variables
Dim searchn As Integer ' The value input for the search
If IsNumeric(Sheets("Search").Range("C2")) Then
searchn = Sheets("Search").Range("C2").Value
End If
Dim i As Integer ' Simple counter for loops (column number)
i = 0
Dim j As Integer 'Simple counter for loops (row number)
Dim Data As Worksheet ' Define the search area as all of the sheet MC Library
Worksheets("MC library").Activate
Set Data = Sheets("MC library")
Dim loc As Range
Dim rang As Range
Dim spce As Range
Dim mass() As Single
Dim Found As Variant
Set rang = Sheets("MC library").Range("C3:Z500")
Set loc = Sheets("MC library").Range("C3")
On Error Resume Next
Do
Set Found = rang.Find(What:=searchn, LookIn:=xlValues, LookAt:= _
xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlNext, MatchCase:= _
False, SearchFormat:=False)
MsgBox (Found)
Set loc = Sheets("MC library").Range(Found.Address)
If Found > 0 Then
ReDim Preserve mass(i)
mass(i) = Found
i = i + 1
Set rang = Sheets("MC library").Range(loc, "Z500")
End If
Loop Until Found Is Nothing
End Sub
[/code]
This is all a work in progress so there's a few things in there that aren't relevant yet. The Do loop is where the real problems kick in.
searchn calls in a value from a cell that is an input for the search from the user and is typically a four-digit number. The MsgBox line is simply used for debugging and won't be in the final version.
Any suggestions and help would be greatly appreciated. The biggest issue (I think), is finding a way to store the location of a cell in a variable and then using that to change the range as I go.
Sub SearchLibrary()
Dim searchn
Dim shtData As Worksheet
Dim hits As Collection, hit
searchn = Sheets("Search").Range("C2").Value
If Len(searchn) = 0 Or Not IsNumeric(searchn) Then
MsgBox "Search term should be numeric!", vbExclamation
Exit Sub
End If
Set shtData = Worksheets("MC library")
Set hits = FindAll(shtData.Range("C3:Z500"), searchn)
For Each hit In hits
Debug.Print hit.Address, hit.Value
Next hit
End Sub
Public Function FindAll(rng As Range, v) As Collection
Dim rv As New Collection, f As Range
Dim addr As String
Set f = rng.Find(what:=v, after:=rng.Cells(1), _
LookIn:=xlValues, LookAt:=xlPart, SearchOrder:=xlByRows, _
SearchDirection:=xlNext, MatchCase:=False)
If Not f Is Nothing Then addr = f.Address()
Do Until f Is Nothing
rv.Add f
Set f = rng.FindNext(after:=f)
If f.Address() = addr Then Exit Do
Loop
Set FindAll = rv
End Function
here is a find routine that works
Sub findAll()
Dim aaa As Range
With ActiveSheet.Cells
Set aaa = .Find(3, LookIn:=xlValues) ' find number 3
If Not aaa Is Nothing Then
firstFind = aaa.Address
Do
Debug.Print aaa.Address, aaa.Value
Set aaa = .FindNext(aaa)
Loop While aaa.Address <> firstFind
End If
End With
End Sub

Setting variables VBA

complete novice here
I started some VBA a few days ago, I have simple question but cant seem to find what I am doing wrong.
I am trying to make a button which will take the coordinates of the active cell and compare them to another worksheet to retrieve a specific value from another table.
I set variables to the active cell column and row, I want to do this so I can later compare these locations to another worksheet and get the value at a specified position on another worksheet.
So far I have written simply what I could find on the internet as I have no formal training.
The msgbox at the end is just to test whether or not it actually picks up the reference.
Sub CommandButton1_Click()
Dim Arow As Range
Dim Acol As Range
Set Arow = Worksheets("Sheet1").Range(ActiveCell.Row)
Set Acol = Worksheets("Sheet1").Range(ActiveCell.Column)
MsgBox (Arow)
End Sub
So far I have error run-time error '1004' Application defined or object defined error highlighting the 4th Row. If anyone could help me solve this or redirect me to some help it would be much appreciated.
I think this won't work, you should put there
Set arow = Worksheets("Sheet1").Range(ActiveCell.Row & ":" & ActiveCell.Row)
Putting there simply number won't work. For the column, you should put there somethong like C:C. For getting letter of column, see this qestion: Function to convert column number to letter?
For more information about Range property, please see official documentation https://msdn.microsoft.com/en-us/library/office/ff836512.aspx.
The thing is, that you have to supply either the address in so called A1 reference, which is "A1", or "$A$1" or name of cell, etc, or you have to supply two Range objects, such as two cells Worksheets("Sheet1").Range(Worksheets("Sheet1").Cells(1,1), Worksheets("Sheet1").Cells(2,2)), which defines area starting with upper-left corner in first parameter and lower right in second parameter.
ActiveCell.Row and ActiveCell.Column returns you some Integer value representing number of row and column, i.e. if you point cell B4, ActiveCell.Row would return 4, and ActiveCell.Column gonna return 2. An Range() property need as an argument whole adress for some range, i.e. Range("C6") or Range("G3:J8").
When you have your column as a number, you can use Cells() property for pointing first and last cell in your range, i.e. Range(Cells(2, 4), Cells(6, 8) would be the same range as Range("D2:H6").
Following this, one of the ways that you can do what you have described is:
Sub CommandButton1_Click()
Dim Rng As Range
Set Rng = Worksheets("Sheet1").Cells(ActiveCell.Row, ActiveCell.Column)
End Sub
Now you have under variable Rng an Range of the same coordinates as ActiveCell, but in Sheet1. You can pass some value into i.e Rng.Value = "Hello World", paste something with Rng.PasteSpecial xlPasteAll etc.
if you want the value from other sheet at the same location as activeCell, use this code,
Private Sub CommandButton1_Click()
valueFromOtherSheet = Sheets("Sheet2").Range(ActiveCell.Address)
MsgBox (valueFromOtherSheet)
End Sub
Like the others have said, it's just about knowing your variable types. This is another way you could achieve what you want
Sub CommandButton1_Click()
Dim Acell As Range
Set Acell = Worksheets("Sheet2").Range(ActiveCell.Address)
MsgBox "Value on ActiveSheet: " & ActiveCell.Value & vbNewLine & _
"Value on Sheet2: " & Acell.Value
End Sub
Thank you everyone for the help and clarification, In the end I was able to come up with some code that seems to do what I need it to.
Private Sub CommandButton1_Click()
Dim cabDate As Range
Dim searchCol As Integer
Dim newindex As Range
Set cabDate = WorksheetFunction.Index(Range("A1:O9999"), ActiveCell.Row, 2)
searchCol = ActiveCell.Column
Set newindex = WorksheetFunction.Index(Worksheets("Deadlines").Range("A1:O9999"), cabDate.Row, searchCol)
MsgBox (newindex)
End Sub
I wasn't aware about conflicting data types so thank you all for the assistance.

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