This may seem like a simple question but Im very new to VBA and Im not sure why I'm receiving the error.
Dim c As String
c = Sheet2.Range("B3:B54").Find("NLwk01")
Error code is 91: Object variable or With block variable not set.
I thought I should've maybe used cells instead of range, but that gives another error with
Error code 5: Invalid procedure call or argument.
As it was mentioned in comment thread, Excel VBA Find() function returns the Range object. Therefore, pertinent to you particular example, it could be coded as in the following sample snippet:
Sub FindRowIndex()
Dim c
Dim rowIdx As Integer
Dim cellValue As String
'return Range object if found
Set c = Sheet2.Range("B3:B54").Find("NLwk01")
If Not c Is Nothing Then
'return the row index (shown as an example)
rowIdx = c.Row
'return the same string used as search criterion "NLwk01"
cellValue = c.Value
End If
End Sub
Pertinent to your case search area ("B3:B54") the rowIdx can be declared As Integer; for extended area you may use Long.
Also, as mentioned in comments thread, you may declare: Dim c As Range.
Hope this may help.
Related
I'm working on a piece of code to extract the nominal size of a pipeline from it's tagname. For example: L-P-50-00XX-0000-000. The 50 would be it's nominal size (2") which I would like to extract. I know I could do it like this:
TagnameArray() = Split("L-P-50-00XX-0000-000", "-")
DNSize = TagnameArray(2)
But I would like it to be a function because it's a small part of my whole macro and I don't need it for all the plants I'm working on just this one. My current code is:
Sub WBDA_XXX()
Dim a As Range, b As Range
Dim TagnameArray() As String
Dim DNMaat As String
Dim DN As String
Set a = Selection
For Each b In a.Rows
IntRow = b.Row
TagnameArray() = Split(Cells(IntRow, 2).Value, "-")
DN = DNMaat(IntRow, TagnameArray())
Cells(IntRow, 3).Value = DN
Next b
End Sub
Function DNMaat(IntRow As Integer, TagnameArray() As String) As Integer
For i = LBound(TagnameArray()) To UBound(TagnameArray())
If IsNumeric(TagnameArray(i)) = True Then
DNMaat = TagnameArray(i)
Exit For
End If
Next i
End Function
However this code gives me a matrix expected error which I don't know how to resolve. I would also like to use the nominal size in further calculations so it will have to be converted to an integer after extracting it from the tagname. Does anyone see where I made a mistake in my code?
This is easy enough to do with a split, and a little help from the 'Like' evaluation.
A bit of background on 'Like' - It will return TRUE or FALSE based on whether an input variable matches a given pattern. In the pattern [A-Z] means it can be any uppercase letter between A and Z, and # means any number.
The code:
' Function declared to return variant strictly for returning a Null string or a Long
Public Function PipeSize(ByVal TagName As String) As Variant
' If TagName doesn't meet the tag formatting requirements, return a null string
If Not TagName Like "[A-Z]-[A-Z]-##-##[A-Z]-####-###" Then
PipeSize = vbNullString
Exit Function
End If
' This will hold our split pipecodes
Dim PipeCodes As Variant
PipeCodes = Split(TagName, "-")
' Return the code in position 2 (Split returns a 0 based array by default)
PipeSize = PipeCodes(2)
End Function
You will want to consider changing the return type of the function depending on your needs. It will return a null string if the input tag doesnt match the pattern, otherwise it returns a long (number). You can change it to return a string if needed, or you can write a second function to interpret the number to it's length.
Here's a refactored version of your code that finds just the first numeric tag. I cleaned up your code a bit, and I think I found the bug as well. You were declaring DNMAAT as a String but also calling it as a Function. This was likely causing your Array expected error.
Here's the code:
' Don't use underscores '_' in names. These hold special value in VBA.
Sub WBDAXXX()
Dim a As Range, b As Range
Dim IntRow As Long
Set a = Selection
For Each b In a.Rows
IntRow = b.Row
' No need to a middleman here. I directly pass the split values
' since the middleman was only used for the function. Same goes for cutting DN.
' Also, be sure to qualify these 'Cells' ranges. Relying on implicit
' Activesheet is dangerous and unpredictable.
Cells(IntRow, 3).value = DNMaat(Split(Cells(IntRow, 2).value, "-"))
Next b
End Sub
' By telling the function to expect a normal variant, we can input any
' value we like. This can be dangerous if you dont anticipate the errors
' caused by Variants. Thus, I check for Arrayness on the first line and
' exit the function if an input value will cause an issue.
Function DNMaat(TagnameArray As Variant) As Long
If Not IsArray(TagnameArray) Then Exit Function
Dim i As Long
For i = LBound(TagnameArray) To UBound(TagnameArray)
If IsNumeric(TagnameArray(i)) = True Then
DNMaat = TagnameArray(i)
Exit Function
End If
Next i
End Function
The error matrix expected is thrown by the compiler because you have defined DNMaat twice: Once as string variable and once as a function. Remove the definition as variable.
Another thing: Your function will return an integer, but you assigning it to a string (and this string is used just to write the result into a cell). Get rid of the variable DN and assign it directly:
Cells(IntRow, 3).Value = DNMaat(IntRow, TagnameArray())
Plus the global advice to use option explicit to enforce definition of all used variables and to define a variable holding a row/column number always as long and not as integer
I have previously used this method within my program to insert a formula which was a lot larger however I am receiving "run-time error code 1004 unable to set the FormulaArray property of the range class" with this one:
Sub TEST()
Dim QFormula As String
Dim QCountF As String
Dim QCountF2 As String
Dim WStart As String
Dim MoreDash As String
Dim ValueF As String
QFormula = "=IF($K3=4,IF(_Q_>0,1,_M_),IF($K3=2,IF(_Q_>0,1,IF(COLUMN(P3)-MATCH(_S_,$A$1:P$1,0)>=8,IF(_Q2_>0,1,_M_),_M_)),IF(_Q_>0,1,IFERROR(IF((COLUMN(P3)-MATCH(_S_,$A$1:P$1,0)+1)-_V_<=13,1,_M_),_M_))))"
QCountF = "COUNTA(OFFSET(INDIRECT(ADDRESS(ROW(P3),COLUMN(P3)-4)),0,1,1,3))"
QCountF2 = "COUNTA(OFFSET(INDIRECT(ADDRESS(ROW(P3),COLUMN(P3)-8)),0,1,1,3))"
WStart = """START"""
MoreDash = """-"""
ValueF = "VALUE(MATCH(1,INDIRECT(ADDRESS(ROW(P3),MATCH(_S_,$A$1:P$1,0))):O3,0))"
With ActiveCell
.FormulaArray = QFormula
.Replace "_Q_", QCountF
.Replace "_Q2_", QCountF2
.Replace "_S_", WStart
.Replace "_M_", MoreDash
.Replace "_V_", ValueF
End With
End Sub
I am at a loss as to where I have gone wrong this time and am hoping you can help.
Thanks in advance
Taken straight from msdn site:
If you use this property to enter an array formula, the formula must use the R1C1 reference style, not the A1 reference style (see the example).
Worksheets("Sheet1").Range("E1:E3").FormulaArray = "=Sum(R1C1:R3C3)"
Update edit:
As you stated in comments even though msdn states you have to use R1C1 reference the A1 reference works too.
The actual issue in your code is:
ValueF = "VALUE(MATCH(1,INDIRECT(ADDRESS(ROW(P3),MATCH(_S_,$A$1:P$1,0))):O3,0))"
you have another _S_ within the replacement code this never gets changed as its already past the _S_ .Replace.
Fix:
ValueF = "VALUE(MATCH(1,INDIRECT(ADDRESS(ROW(P3),MATCH(""START"",$A$1:P$1,0))):O3,0))"
I have an excel sheet with values in a range which I want to perform some calculations on in vb.net. I pass that range to vb.net using COM. When I try editing the values of the range, nothing happens.
My question seems to be very similar to these questions, but I can't quite figure out what step I am missing.
How to edit cell value in VB.net - Using .Interop.Excel
VB.net Office Solution - Accessing value in named Range in a Worksheet
VBA CODE:
Function MyTestRange (Byref myrng as range)
Set classLib = New VBProject.CClass
MyTestRange = classLib.MyTestRange(myrng)
End Function
VB.NET code
Imports Microsoft.Office.Interop.Excel
Public Class CClass
Function MyTestRange(ByRef myrng As Range) As Double
Dim newrng As Range
Dim b As Integer = myrng.Rows.Count
Dim i As Integer
newrng = myrng
For i = 1 To b
newrng.Value2(i, 1) = myrng.Value2(i, 1) + 1
Next i
MyTestRange = newrng.Value2(1, 1)
End Function
End class
While this code doesn't generate an error, it doesn't change the values in newrng.
Edit:
I have tried many iterations based on the link provide, but always get the same error :
An exception of type 'System.Runtime.InteropServices.COMException' occurred in mscorlib.dll but was not handled in user code
Additional information: Exception from HRESULT: 0x800A03EC
This is the simplest way to generate the error:
Dim c As Object
c = myrng.Value
myrng.Value = c
Hence amending the values to something new, seems out of reach!
Edit 2:
After chatting with user Mat's Mug, I tried to edit a value in a range, purely in VBA.
This sub works perfectly:
Sub rangesub()
Dim example As Range
Set example = Range("A1:A4")
example.Value = Application.Transpose(Array(1, 2, 3, 4))
End Sub
Excel functions also seem able to handle passing ranges:
Function SimpleCopyRange(myrng)
SimpleCopyRange = myrng
End Function
A very simple combination of this code works:
Function EditRange(myrng)
Dim example As Range
Set example = Range("A1:A4")
EditRange = example
End Function
However there is an unspecified error if I try to edit the range:
Function EditRange(myrng)
Dim example As Range
Set example = Range("A1:A4")
example.Value = Application.Transpose(Array(1, 2, 3, 4))
EditRange = example
End Function
The application.transpose part obviously adds a layer of complexity, and can be replaced with example.Value = 8 without any changes in results.
Looking at this question/answer:
Excel VBA: Iterating over range parameter and change cell values
It appears that a UDF can only change the calling cell.
I think therefore this method is impossible.
The workaround is to to rewrite the code to work with an array, assign the range to an array, edit the array and pass it to the new function.
I'm writing some VBA functions in Excel that compute word values and cross sums of the input.
I'm passing the input as Public Function cross_sum(myRange As Range) As Integer to them so that they take cell references as input, e.g. =cross_sum(A1). Works fine.
However when I try to chain two functions like =cross_sum(word_value(A1)) I run into th VALUE error because word_value() returns an Integer value and not the Range cross_sum() is set to expect. However I did not find a way to cast an Integer (or String) into a Range.
As Excel's built-in functions support chaining as well as Range input I wonder how.
Unfortunately this is my first VBA project so I wonder if and how to cast or what type to choose to get this working both ways.
Any pointers appreciated!
TIA,
JBQ
You can pass Variant to a function and the function can determine the type of input:
Public Function Inputs(v As Variant) As String
If TypeName(v) = "Range" Then
MsgBox "you gave me a range"
Else
MsgBox "you gave me a string"
End If
Inputs = "done"
End Function
Sub MAIN()
Dim st As String
Dim rng As Range
st = "A1"
Set rng = Range(st)
x = Inputs(st)
x = Inputs(rng)
End Sub
Without your code, it is hard to know what you could change. That being said...
There is not a way to convert an integer to a range. You would have to create a function to do so if that is what you desired.
You could create a converter function, maybe titled IntegerToRange, that takes an integer and after some logic (maybe 1 = "A1", 2 = "A2" or something), will return a range. Your cell formula would then be =cross_sum(IntegerToRange(word_value(A1))
Alternatively, you could modify your word_value function to return a range instead of an integer. Your cell formula would then be =cross_sum(word_value(A1).
I'm having some trouble with syntax options while writing a VBA Macro for Excel. In VBA you can call a method on an object in two different ways:
foo.bar(arg1, arg2)
or
foo.bar arg1, arg2
I absolutely detest the second sort of syntax because I find it lacks any sort of clarity, so I normally adhere to the first option. However, I've come across a situation where using the first option creates an error, while the second executes fine. (This may perhaps be an indicator of other problems in my code.) Here is the culprit code:
Function GetFundList() As Collection
Dim newFund As FundValues
Range("A5").Select
Set GetFundList = New Collection
While Len(Selection.Value)
Set newFund = New FundValues
' I set the fields of newFund and move Selection
The problem is in this next line:
GetFundList.Add newFund
Wend
End Function
FundValues is a class I created that is essentially just a struct; it has three properties which get set during the loop.
Basically, when I call GetFundList.Add(newFund) I get the following error:
Run-time error '438':
Object doesn't support this property or method
But calling GetFundList.Add newFund is perfectly fine.
Does anyone understand the intricacies of VBA well enough to explain why this is happening?
EDIT: Thanks much for the explanations!
Adding items to a collection is not defined as a function returning a value, but as a sub routine:
Public Sub Add( _
ByVal Item As Object, _
Optional ByVal Key As String, _
Optional ByVal { Before | After } As Object = Nothing _
)
When calling another sub routine by name and sending arguments (without adding the "Call" statement), you are not required to add parentheses.
You need to add parentheses when you call a function that returns a value to a variable.
Example:
Sub Test_1()
Dim iCnt As Integer
Dim iCnt_B As Integer
Dim iResult As Integer
iCnt = 2
iCnt_B = 3
fTest_1 iCnt, iResult, iCnt_B
End Sub
Public Function fTest_1(iCnt, iResult, iCnt_B)
iResult = iCnt * 2 + iCnt_B * 2
End Function
Sub Test_2()
Dim iCnt As Integer
Dim iCnt_B As Integer
Dim iResult As Integer
iCnt = 2
iCnt_B = 3
iResult = fTest_2(iCnt, iCnt_B)
End Sub
Public Function fTest_2(iCnt, iCnt_B)
fTest_2 = iCnt * 2 + iCnt_B * 2
End Function
Let me know if not clear.
This Daily Dose of Excel conversation will be helpful.
When you use the parentheses you are forcing VBA to evaluate what's inside them and adding the result to the collection. Since NewFund has no default property - I assume - the evaluation yields nothing, so can't be added. Without the parentheses it evaluates to the instance of the class, which is what you want.
Another example. This:
Dim coll As Collection
Set coll = New Collection
coll.Add Range("A1")
Debug.Print coll(1); TypeName(coll(1))
and this ...
coll.Add (Range("A1"))
Debug.Print coll(1); TypeName(coll(1))
... both yield whatever is in A1 in the debug.window, because Value is Range's default property. However, the first will yield a type of "Range", whereas the type in the 2nd example is the data type in A1. In other words, the first adds a range to the collection, the 2nd the contents of the range.
On the other hand, this works:
Dim coll As Collection
Set coll = New Collection
coll.Add ActiveSheet
Debug.Print coll(1).Name
... and this doesn't:
coll.Add (ActiveSheet)
Debug.Print coll(1).Name
because ActiveSheet has no default property. You'll get an runtime error 438, just like in your question.
Here's another way of looking at the same thing.
Let assume that cell A1 contains the string Hi!
Function SomeFunc(item1, item2)
SomeFunc = 4
End Function
Sub Mac()
' here in both of the following two lines of code,
' item1 will be Variant/Object/Range, while item2 will be Variant/String:
SomeFunc Range("A1"), (Range("A1"))
Let i = SomeFunc(Range("A1"), (Range("A1")))
'this following is a compile syntax error
SomeFunc(Range("A1"), (Range("A1")))
' while here in both the following two lines of code,
' item1 will be Variant/String while item2 will be Variant/Object/Range:
SomeFunc ((Range("A1")), Range("A1")
Let j = SomeFunc((Range("A1")), Range("A1"))
'this following is a compile syntax error
SomeFunc((Range("A1")), Range("A1"))
Set r = Range("A1") ' sets r to Variant/Object/Range
Set r = (Range("A1")) ' runtime error 13, type mismatch; cannot SET r (as reference) to string "Hi!" -- Strings are not objects in VBA
Set r = Range("A1").Value ' runtime error (same)
Let r = Range("A1") ' set r to "Hi!" e.g. contents of A1 aka Range("A1").Value; conversion to value during let = assignment
Let r = (Range("A1")) ' set r to "Hi!" e.g. contents of A1 aka Range("A1").Value; conversion to value by extra ()'s
Let r = Range("A1").Value ' set r to "Hi!" by explicit use of .Value
End Sub
I only add this to help illustrate that there are two things going on here, which could be conflated.
The first is that the () in an expression that converts the item to its Value property as stated above in other answers.
The second is that functions invoked with intent to capture or use the return value require extra () surrounding the whole argument list, whereas functions (or sub's) invoked without intent to capture or use the return value (e.g. as statements) must be called without those same () surrounding the argument list. These surrounding () do not convert the argument list using .Value. When the argument list has only one parameter, this distinction can be particularly confusing.