How can range both be a class and a property in VBA? - vba

In the line of code below i am going down in the vba object hierarchy and assigning the cell A1 the value 5. Since in the object browser when i click on the range class and then on the cells property, it says Property Cells As Range. I can see that cells is a property of the range class, but when it says that Property Cells as Range, does that mean that the property cells is the same as the range class? If so, how can range both be a class and a property?
Excel.Application.ActiveSheet.Range("A1").Value = 5

I believe this is to do with the fact that you can have ranges within ranges. In your example, .Range("A1") is a single-cell range so the cells property isn't relevant, but the most common use of the Range.Cells property would be in a loop:
Dim OuterRange as Range, InnerRange as Range 'variables
Set OuterRange = Activesheet.Range("A1:Z10")
'Example 1:
For Each InnerRange in OuterRange
'InnerRange will be a single cell in size
Debug.print ThisCell.Value
Next
'Example 2:
Dim a As Long
a = 1
Set InnerRange = OuterRange.Cells(a)
Do While InnerRange <> ""
debug.print InnerRange.Formula
a = a + 1
Loop
The key points this demonstrates are as follows:
Worksheet.Range(....) can refer to a range of any size, including but not limited to a single-cell range
Worksheet.range(...).Cells(x) refers to Cell number x within Worksheet.Range(...), as a single-cell range. You could also use Worksheet.Range(...).Cells.Count to get the number of cells in the range.
This allows you to write code that will loop through a variable range cell-by-cell no matter what the size and dimensions of the range are

Related

Excel VBA Worksheets.Cells returning value instead of range

The following line is not working:
If Worksheets(Specialist).Cells(projectrow, WeekLoop + 4).Interior.Color = ReferenceCellColorPlanned.Interior.Color Then
where "ReferenceCellColorPlanned" is an user-input range in the formula
where "Specialist" is a string (and the worksheet does exist)
where "projectrow" and "WeekLoop" are integers
The problem is it always goes through the "If" criteria, no matter what the actual background is. So I tried to debug and set the following (simplified code, only taking out the bits that are needed)
Dim Cel1 as Range
Set Cel1 = Worksheets(Specialist).Cells(projectrow, WeekLoop + 4)
If Cel1.Interior.Color = ....
Then what I noticed is Cel1 actually returns a string value of what is the value of the cell, instead of the range value (which I find weird since as far as I understand, "Cells" is by default a range object and I declared Cel1 as a Range Variable).
Kindly help me understand why worksheets.cells is returning a string instead of a range, and how to make it return the range so I can check its background color. Thanks!
EDIT: I've always tried worksheets.Range(Cells()) as well, and it doesn't work either
Excel and VBA are in general user-friendly. Thus, the object Cell returns something, that would make sense to the user – its value, and not something a bit useless like the address of the object, which would someone working with Java expect (see: What's the simplest way to print a Java array?):
int[] intArray = new int[] {1, 2, 3, 4, 5};
System.out.println(intArray); // prints something like '[I#3343c8b3'
If you are familiar with Python, consider that the object Cells have a __repr__ method implemented, which returns their value - What is the difference between __str__ and __repr__?
In VBA, the __repr__ is achieved with the Default Member attribute:
Default Member (CPearson)
Is "Value" actually the default property of the Range object?
VBA Attributes - The High End VBA (MySite)
VB Attributes - What are they and why should we use them (ChristopherMcClellan)
Concerning the .Interior.Color property, you may access it easily like this and see the values you are comparing:
Public Sub TestMe()
Debug.Print Worksheets(1).Cells(1, 1).Interior.Color
Debug.Print Worksheets(2).Range("A10").Interior.Color
End Sub
Even though the Cell is returning its Value when getting printed via MsgBox, you should still be able to compare the Cell Colors since the variable itself remains an Range Object.
Following sample code works fine for me.
Sub test()
Dim Cel2 As Range
Dim Cel3 As Range
Set Cel2 = Worksheets(1).Cells(1, 1)
Set Cel3 = Worksheets(1).Cells(2, 2)
If Cel2.Interior.Color = Cel3.Interior.Color Then
MsgBox ("YES")
Else
MsgBox ("NO")
End If
End Sub

Assigning a combobox a named list in vba

I'm trying to dynamically assign a list to every combo box based on the values of a specific combo box. The idea is that the user picks a category from the specific combobox and all other combo boxes grab the items from that category in the form of a named list.
So the structure is like
Categories
Category 1
category 2
Category 1
Item 1
Item 2
And so on. I had this working on a fake set of names, but now that I'm using real named ranges, the code breaks. It is breaking on "For Each rng In ws.Range(str)" and stating that "method 'range' of object '_worksheet' failed.
This code works. Or worked. Then I changed ws to point to a different sheet of named ranges and now nothing works.
The value of CBOCategory is any value from a list of all named ranges, but it seems like Excel isn't seeing any of them! I tried to trigger even a listfill assignment instead of adding each item and got a similar error
Private Sub CBOCategory_Change()
'Populate dependent combo box with appropriate list items
'according to selection in cboCategoryList.
Dim rng As Range
Dim ws As Worksheet
Dim str, temp, cbName As String
Dim counter As Integer
Set ws = Worksheets("Item Master")
Dim obj As OLEObject
str = CBOCategory.Value
For Each obj In ActiveSheet.OLEObjects
If obj.Name = "CBOCategory" Then
' nothing
Else
temp = obj.Object.Value
obj.Object.Value = ""
For Each rng In ws.Range(str)
obj.Object.AddItem rng.Value
Next rng
obj.Object.Value = temp
End If
'MsgBox ("updated!")
Next obj
End Sub
The code works fine. The root cause of the issue is that the named ranges were being dynamically set by a formula. The formulas were not calculating properly when the code ran, so vba could not use a dynamically set named range to find another, also dynamically set named range.
The solution is to explicitly set the named ranges. Then the code works fine.

Copy a link if cell value matches entry in another list

There is a column with blocks of file names, and there is a column with keys and values:
I have to assign the link "www.111.com" to all AAAAA.jpg areas, "www.222.com" to BBBBB.jpg areas, etc.
Result:
How can be this done?
I think the following VBA code will help you. It does these steps:
Declare a range ("myRange") and set it to cell A1 (the top cell of your list of .JPGs)
Declare a variant ("hText")
Lookup the value in "myRange" in the lookup table at D:E (change to suit your workbook). Store the value in "hText"
Check if hText is an error (i.e., the value was not found in the lookup table). If it was as error, skip the cell. If it wasn't an error, go to step 5.
Add a hyperlink to the current "myRange" cell. Use the hText as the address, use the text of the current "myRange" cell as the displayed text.
Move "myRange" to the next cell down. Loop steps 3-6 until it reaches an empty cell.
Note that the loop will stop when it reaches an empty cell, so if there is a gap in your list it will not reach the bottom. Also, note that any values that are not found in the lookup table will be skipped (no hyperlink added).
Run this code while the sheet with the list of .JPGs is selected.
Sub AddHyperlinks()
Dim myRange As Range
Set myRange = Range("A1")
Dim hText As Variant
Do Until IsEmpty(myRange)
hText = Application.VLookup(myRange.Value, Worksheets("Sheet1").Range("D:E"), 2, False)
If IsError(hText) Then
hText = ""
Else
ActiveSheet.Hyperlinks.Add Anchor:=myRange, Address:=hText, TextToDisplay:=myRange.Text
hText = ""
End If
Set myRange = myRange.Offset(1, 0)
Loop
End Sub

Searching and Returning bold values in VBA

I know that this probably isn't the most ideal way to to do this but just bear with me.
I have a document with a few tables on it. I'm using a userform to search the tables/sub-categories and return the relevant values. I want to select the sub categories with a range of option buttons on a userform, these will in turn set the range for the search function to look within. I also want to dynamically update the option buttons if a new table was to be added or anything along those lines.
The only thing that differentiates the title of a sub-category/table, and the items within it, is that the title of a sub-category/table is bold. So what I'm looking to do is search the first column of the spreadsheet and return the names of any entries in bold. These values are then used to set the names of the option buttons :).
The following function is my attempt at finding the text entities in column a that are in bold, returning them and setting each to an individual variable to be used in another function. The bold1 .... variables are all globally defined variables as I need them in another sub, as is the page variable which contains the relevant page to be used. Currently the code returns an error stating "variable or with block not set" and using the debugger I can see that bold1 .... and all the other boldx variables have no value set. Does anybody know whats going on/how to fix this function.
Thanks in advance :)
Sub SelectBold()
Dim Bcell As Range
For Each Bcell In Worksheets(Page).Range("A1:A500")
If Bcell.Font.Bold = True Then
Set bold1 = Bcell
End If
Next
End Sub
EDIT: I simplified the above function, to remove clutter and help narrow in on the issue. I want the above function to store the contents of the found cell (any cell in the document in bold at this stage) in the variable bold1
This will return an array of values from bold cells in column A of Page.
You can fill a combo or list box with theses values using their list property.
ComboBox1.List = getSubCategories("Sheet1")
Function getSubCategories(Page As String) As String()
Dim arrSubCategories() As String
Dim count As Long
Dim c As Range
With Worksheets(Page)
For Each c In .Range("A2", .Range("A" & Rows.count).End(xlUp))
If c.Font.Bold Then
ReDim Preserve arrSubCategories(count)
arrSubCategories(count) = c.Value
count = count + 1
End If
Next
End With
getSubCategories = arrSubCategories
End Function
you may find useful to have a Range returned with subcategories cells found:
Function SelectBold(Page As String, colIndex As String) As Range
With Worksheets(Page)
With .Range(colIndex & "1", .Cells(.Rows.Count, colIndex).End(xlUp)).Offset(, .UsedRange.Columns.Count)
.FormulaR1C1 = "=if(isbold(RC[-1]),"""",1)"
.Value = .Value
If WorksheetFunction.CountA(.Cells) < .Rows.Count Then Set SelectBold = Intersect(.SpecialCells(xlCellTypeBlanks).EntireRow, .Parent.Columns(1))
.Clear
End With
End With
End Function
Function IsBold(rCell As Range)
IsBold = rCell.Font.Bold
End Function
to be possibly exploited as follows:
Option Explicit
Sub main()
Dim subCategoriesRng As Range, cell As Range
Set subCategoriesRng = SelectBold(Worksheets("bolds").Name, "A") '<--| pass worksheet name and column to search in
If Not subCategoriesRng Is Nothing Then
For Each cell In subCategoriesRng '<--| loop through subcategories cells
'... code
Next cell
End If
End Sub

Get User Selected Range

How can I get a range of cells selected via user mouse input for further processing using VBA?
Selection is its own object within VBA. It functions much like a Range object.
Selection and Range do not share all the same properties and methods, though, so for ease of use it might make sense just to create a range and set it equal to the Selection, then you can deal with it programmatically like any other range.
Dim myRange as Range
Set myRange = Selection
For further reading, check out the MSDN article.
You can loop through the Selection object to see what was selected. Here is a code snippet from Microsoft (http://msdn.microsoft.com/en-us/library/aa203726(office.11).aspx):
Sub Count_Selection()
Dim cell As Object
Dim count As Integer
count = 0
For Each cell In Selection
count = count + 1
Next cell
MsgBox count & " item(s) selected"
End Sub
This depends on what you mean by "get the range of selection". If you mean getting the range address (like "A1:B1") then use the Address property of Selection object - as Michael stated Selection object is much like a Range object, so most properties and methods works on it.
Sub test()
Dim myString As String
myString = Selection.Address
End Sub