build method command in cell and run it in vba - vba

I have the following command which runs correctly if placed inside a macro in vba:
Set elements = doc.getElementsByClassName("media-body")
I try to have this command running while taking the information from within a cell. Thus I place
doc.getElementsByClassName("media-body")
in cell D5 , but when I run:
Set elements = evaluate("Range(""D5"")") it throws an error (Run-time error '424': Object required.
Is there a way to solve it?

No, there isn't a way to solve it. Excel.Application.Evaluate only works with a subset of Excel objects. From the documentation:
Remarks
The following types of names in Microsoft Excel can be used
with this method:
Formulas. A1-style references. You can use any
reference to a single cell in A1-style notation. All references are
considered to be absolute references.
Ranges. You can use the range,
intersect, and union operators (colon, space, and comma, respectively)
with references. Defined names. You can specify any name in the
language of the macro.
External references. You can use the ! operator
to refer to a cell or to a name defined in another workbook ? for
example, Evaluate("[BOOK1.XLS]Sheet1!A1").
Chart Objects. You can
specify any chart object name, such as "Legend", "Plot Area", or
"Series 1", to access the properties and methods of that object. For
example, Charts("Chart1").Evaluate("Legend").Font.Name returns the
name of the font used in the legend.
The reason that you get an error 424 is because doc is an object reference in the code you are trying to run from the cell. Evaluate has no clue what doc is, because there is no way to set it to an instance from within a cell.
The only way to do anything meaningful with the cell contents in the question would be to parse the code in the cell and basically run it through an VBA-VBA interpreter.
EDIT
If I understand correctly what you're after (see the comments), what you'd really like is a way to generically search for HTML elements based on name and type. I'd approach this by selecting how to search the element name based on a type parameter. The example below assumes that the target cell (in row sourceRow and column sourceCol) contains the element name, ie "media-body", and the cell to the right contains its type - i.e. "ClassName".
Public Function GetElement(targetSheet As Worksheet, doc As HTMLDocument, _
sourceRow As Long, sourceCol As Long) As IHTMLElement
With targetSheet
'Get the element name from the passed cell.
Dim elementName As String
elementName = .Cells(sourceRow, sourceCol)
'Get the element type from the adjacent cell.
Dim elementType As String
elementType = .Cells(sourceRow, sourceCol + 1)
Select Case elementType
Case "ClassName"
Set GetElement = doc.getElementsByClassName(elementName)
Case "Id"
Set GetElement = doc.getElementById(elementName)
Case "Name"
Set GetElement = doc.getElementsByName(elementName)
'...
End Select
End With
End Function
This could just as easily be accomplished with a comma delimited string in a single cell - something like "media-body,ClassName", or several other methods but this is the direction I'd go.

Replace your:
Set elements = evaluate("Range(""D5"")")
With:
Set elements = Evaluate("D5")

Related

How do I use RefersToRange?

Can anyone tell me how to use RefersToRange in vba? and what and when is the need of it.
Please provide with simple example first.
Thanks in advance.
In Excel, there is the concept of a named range, that is a range of cells which has a name attached to it. This is represented by the Name object.
The RefersToRange method:
Returns the Range object referred to by a Name object.
For example, let's say I want to read the values only in the print area of the current worksheet. I need the Name object in order to access the print area, but I can't do anything useful with it; if I want to do something useful I have to access the range of cells referred to by that name:
Dim n As Name
Set n = ActiveWorkbook.Names("Print_Area")
Dim rng As Range
Set rng = n.RefersToRange
Dim values() As Variant
values = rng 'I can't read values with a Name object, but I can with a Range object
One thing I have discovered is that sometimes referring to a named range in this fashion:
ThisWorkbook.Range("DownloadPictures").Value = "yes"
results in:
Run-time error '1004':
Method 'Range' of object '_Worksheet' failed
But referring to the named range in this fashion:
ThisWorkbook.Names("DownloadPictures").RefersToRange = "yes"
works fine. My particular circumstance is that the named range ("DownloadPictures") is scoped to the entire workbook, which has several worksheets. I am referring to the name in one of the worksheet's Worksheet_Calculate subroutines. I don't understand why the first method causes an error and the second does not but apparently this is one reason to use the RefersToRange property.
RefersToRange is, one of many ways, to 'point' a range. For example,
ThisWorkbook.Names("DownloadPictures").RefersToRange = "yes"
the above code, points to the range, which can be a cell, named "DownloadPictures" and sets it value to "yes". As a matter of fact, I'd rather use,
ThisWorkbook.range("DownloadPictures").Value = "yes"
The 'Name' in MS Excel is similar to the Variant variable type, but unlike the variables you 'Dim' in the VB code, the 'Name' only be used (or you can consider it is only be used) in the Workbook. The 'Name' and variables are not interfered with each other.
If you open the 'Name Manager', the concept becomes easier to understand. The Name can be created to refer to a cell, a range of cell (a range), some value (so called constant), or a formula.
Each Name object represents a defined name for a range of cells. Names can be either built-in names—such as Database, Print_Area, and Auto_Open—or custom names. ---MSDN
As a Name can refer to range, constant or a formula .RefersToRange specifically refer to a range. And return false if not so.

Getting a value from a named range in VBA

I want to retrieve a value from named range. Imagine a named range that has X columns and Y rows. I want to return a value, e.g., from column 2, row 3. The issue I experience is that if I write the code and run it, Excel throws an error. If I write the code into the watch window, it returns fine. See below
...
Dim NamedRange As Variant: NamedRange = Range(NamedRangeName)
...
Dim ReturnValue As Object
Set ReturnValue = NamedRange(RowIndex, ColumnToRetrieveIndex) 'Throws Run-time error 424. Object required
If I write
NamedRange(RowIndex, ColumnToRetrieveIndex)
into the watch window, I can see the correct value of the cell.
I don't know VB much so I guess it's just some kind of syntax error how I want to pass it into the ReturnValue but I just can't figure it out.
Use this
ThisWorkbook.Names("myNamedRange").RefersToRange(1,1)
To get the value from the first cell in the named range "myNamedRange"
With ThisWorkbook.Names you can access all named ranges of all the sheets within the current workbook.
With RefersToRange you get a reference to the actual range.
This looks like a good place to put a handy note (as it's a top search result):
If you name a cell "TopLeft", then Sheets(1).Range("TopLeft").Value gets the contents, and Sheets(1).Range("TopLeft").Offset(2,3).Value gets the value from 2 down, 3 across from there.

Pass a range into a custom function from within a cell

Hi I'm using VBA in Excel and need to pass in the values from two ranges into a custom function from within a cell's formula. The function looks like this:
Public Function multByElement(range1 As String, range2 As String) As Variant
Dim arr1() As Variant, arr2() As Variant
arr1 = Range(range1).value
arr2 = Range(range2).value
If UBound(arr1) = UBound(arr2) Then
Dim arrayA() As Variant
ReDim arrayA(LBound(arr1) To UBound(arr1))
For i = LBound(arr1) To UBound(arr1)
arrayA(i) = arr1(i) * arr2(i)
Next i
multByElement = arrayA
End If
End Function
As you can see, I'm trying to pass the string representation of the ranges. In the debugger I can see that they are properly passed in and the first visible problem occurs when it tries to read arr1(i) and shows as "subscript out of range". I have also tried passing in the range itself (ie range1 as Range...) but with no success.
My best suspicion was that it has to do with the Active Sheet since it was called from a different sheet from the one with the formula (the sheet name is part of the string) but that was dispelled since I tried it both from within the same sheet and by specifying the sheet in the code.
BTW, the formula in the cell looks like this:
=AVERAGE(multByElement("A1:A3","B1:B3"))
or
=AVERAGE(multByElement("My Sheet1!A1:A3","My Sheet1!B1:B3"))
for when I call it from a different sheet.
First, see the comment Remou left, since that's really what you should be doing here. You shouldn't need VBA at all to get an element-wise multiplication of two arrays.
Second, if you want to work with Ranges, you can do that by declaring your function arguments to be of type Range. So you could have
Public Function multByElement(range1 As Range, range2 As Range)
and not need to resolve strings to range references yourself. Using strings prevents Excel from updating references as things get moved around in your worksheet.
Finally, the reason why your function fails the way it does is because the array you get from taking the 'Value' of a multi-cell Range is two-dimensional, and you'd need to acces its elements with two indices. Since it looks like you're intending to (element-wise) multiply two vectors, you would do either
arrayA(i) = arr1(i,1) * arr2(i,1)
or
arrayA(i) = arr1(1,i) * arr2(1,i)
depending on what orientation you expected from your input. (Note that if you do this with VBA, orientation of what is conceptually a 1-D array matters, but if you follow Remou's advice above, Excel will do the right thing regardless of whether you pass in rows or columns, or range references or array literals.)
As an epilogue, it also looks like you're not using 'Option Explicit'. Google around for some rants on why you probably always want to do this.

Display custom document property value in Excel 2007 worksheet cell

I've created a program that creates and populates a custom document property in an Excel 2007 workbook file. However I haven't been able to show the value of this property in a worksheet cell. In Word 2007 you can just select "Insert -> Quick Parts -> Field..." and use the DocProperty field to show the value of the custom field in a document. However I haven't found a similar function in Excel 2007.
Does anybody know how to display the value of a custom document property in an Excel worksheet cell? I would prefer a solution similar to the Word 2007 solution mentioned above. I rather not use a macro/custom code for this.
Unfortunately I believe you need to use an user defined function. Add a new VBA module to your workbook and add this function:
Function DocumentProperty(Property As String)
Application.Volatile
On Error GoTo NoDocumentPropertyDefined
DocumentProperty = ActiveWorkbook.BuiltinDocumentProperties(Property)
Exit Function
NoDocumentPropertyDefined:
DocumentProperty = CVErr(xlErrValue)
End Function
The call to Application.Volatile forces the cell to be updated on each recalculation ensuring that it will pick up changes in the document properties.
The equivalent in Excel would be via formula and I don't think it's possible to extract a document property without code. There are no native functions to pick out document properties. (An alternative could be to store information in workbook/worksheet Names, which ARE accessible via formula)
In VBA you'd have to create a function something like:
Public Function CustomProperty(ByVal prop As String)
CustomProperty = ActiveWorkbook.CustomDocumentProperties(prop)
End Function
and then call it in a formula with =CustomProperties("PropertyName").
There is another subtle point. Formula dependencies only relate to other cells; this formula depends on a custom property. If you update the custom property a pre-existing formula involving CustomProperty will not be updated automatically. The cell will have to be re-evaluated manually or the entire workbook forced through a recalc. Your best chance would be to make the function volatile, which means the formula would be recalc'd on every cell change -- but this still means you only get an update if a cell has been changed.
Select the cell you want to extract
Rename the cell to some useful. From "B1" to "Project_Number".
Open "Advance Properties" click the "Custom" tab. Enter a name for the new property. click "Link to content" the select the cell name from the "Value" pull down list.
I wish i could take cerdit but I found the answer online:
http://pdmadmin.com/2012/03/displaying-custom-property-values-in-excel-using-a-named-range/
You can link a named range to a custom property, but then the custom property reflects the value of the [first cell in the] range. It's effectively read-only; you can change the content of the cell to update the property, but not the other way around.
I know you want to avoid it, but if you want to use the property value in a formula, you'll have to create a custom worksheet function to do so.
I have experienced the same issues other people have. So I will try to comprehensively cover how I addressed it.
First of all, you have no other option than writing a function meant to get whatever you put in a custom or built-in property and make the "problem" cell to point at it this way:
=yourPropertyGettingFunctionName(PropertyName)
PropertyName being a string referring to the name of the custom/built-in property whose value you want to be shown in the cell.
The function could be written (as formerly suggested) as:
Public Function StdProp(ByVal sPropName As String) As String
Application.Volatile
StdProp = ActiveWorkbook.BuiltinDocumentProperties(sPropName).Value
End Function
for a built-in property, or as:
Public Function UsrProp(ByVal sPropName As String) As String
Application.Volatile
On Error GoTo UndefinedProp
UsrProp = ActiveWorkbook.CustomDocumentProperties(sPropName)
GoTo Exit
UndefinedProp:
UsrProp = "n/a"
Exit:
End Function
As already mentioned, including Application.Volatile will allow for a semi-automatic cell contents update.
However, this poses a problem on its own: whenever you open your Excel file, all the cells using such a relationship will get updated and, by the time you exit the file, Excel will ask you for your permission to update it, no matter if you did introduce any change on it or not, because Excel itself did.
In my development group, we use SubVersion as a version control system. In case you inadvertently hit "update" on exit, SVN will notice it and next time you want to commit your changes, the excel file will be included in the pack.
So I decided to use everything at hand to do whatever I needed and avoid, at the same time, this self-update effect I didn't want.
That means using named ranges in combination with property accessing function/s.
Given the fact I can't expect old files to have provision for my new needs, I wrote this function:
Private Function RangeAssign(sRange As String, sValue As String) As Integer
Dim rDest As Range
If RangeCheck(sRange) Then
Set rDest = Range(sRange)
Else
Set rDest = Application.InputBox(sMsg + vbCrLf + vbCrLf + _
"Please, select a cell to get" + vbCrLf + _
"the name " + sRange + " assigned", sCopyRight, Type:=8)
rDest.Name = sRange
End If
rDest.Cells(1, 1).NumberFormat = "#"
rDest.Cells(1, 1).Value = sValue
RangeAssign = True
End Function
It allows for a proper selection of the destination cell. When assigning values to a property (let's say "Author", which happens to be a built-in one), I also update the value stored in the named range, and can write in a cell:
=Author
if I happen to have defined a range named "Author" and filled its "A1" cell with the value for built-in property "Author", which I need to have updated for our own external tracking purposes.
This all didn't happen overnight. I hope it can be of some help.
I used this for extracting the SharePoint properties (based on Martin's answer):
Public Function DocumentProperty(Property As String)
Application.Volatile
On Error GoTo NoDocumentPropertyDefined
DocumentProperty = ActiveWorkbook.ContentTypeProperties(Property).Value
Exit Function
NoDocumentPropertyDefined:
DocumentProperty = CVErr(xlErrValue)
End Function

VBA point variable to range

I want to point to a cell as a range in VBA. I've tried using:
Dim range
range = Sheet("sheet").Range("A1")
But this just returns the value in the range. What I actually want is the range object so I can manipulate it, e.g. by setting range.Value = "Hello"
Any ideas?
First, I strongly recommend you to make explicit declaration of variables in your code mandatory. Go to Tools - Options, in the Editor tab check "Require variable Declaration", or put Option Explicit in the first line of all your scripts.
Second, I think there is a small typo in your code, it should be Sheets.("sheet").
To answer your question, with range = Sheets("sheet").Range("A1") you are assigning a value variable, not an object. Therefore the default variable of the range object is implicitly assigned, which is value. In order to assign an object, use the Set keyword. My full example code looks like this:
Option Explicit
Public Sub Test()
Dim RangeObject As range
Set RangeObject = Sheets("Sheet1").range("A1")
RangeObject.Value = "MyTestString"
End Sub
This should put the text "MyTestString" in cell A1.
Edit: If you are using named ranges, try RangeObject.Value2 instead of RangeObject.Value. Named ranges do not have a Value property.