someExcel VBA: Cannot create a range object successfully - vba

This is my first question here, so bear with me. I'm a security consultant working on a huge firewall migration, for which I got my VBA skill from under a thick layer of dust. So far I have managed to get all my issues resolved by searching, but this issue: I get errors when doing exactly how I find it everywhere.
What I want to do:
I have an array that contains (among other things), strings formatted like this: "A3:P59", representing a cell range.
Now, this are ranges within a table. When I get the address of a certain cell in the table, I want to test if it's in that range.
I wrote a test function:
Function TestCellRange() As Boolean
Dim tbl As ListObject
Dim cell, rng, test As range
Dim range As range
Dim bRow, eRow As Integer
Set tbl = shRulebase.ListObjects("tblBFFirewallRules")
shRulebase.Activate
With shRulebase
cell = tbl.DataBodyRange(5, 1).Address(False, False) 'it's this command that gives me issues
Set range = .range(.Cells(bRow, 1), .Cells(eRow, 16))
Debug.Print cell
'Set rng = shRulebase.range(range)
Debug.Print rng
Set test = Application.Intersect(cell, range(range(A3), range(P59)))
If test Is Nothing Then
MsgBox ("oops")
TestCellRange = False
Else
MsgBox ("yup yup")
TestCellRange = True
End If
End With
End Function
Now whatever I try, I keep getting blocked on the set range:
set range = .Range("A3:P59") -> will return "object required", on the "set test" line (if i use intersect (cell, range))
Set range = range("A3:P59") -> will return object variable or with block variale not set on the same line
Set range = .range(.Cells(bRow, 1), .Cells(eRow, 16)) -> will step through, but debug.print returns a type mismatch and "Set test = Application.Intersect(cell, range)" returns a "object required"
Any help would be really appreciated...I'm all to familiar with networks ip's and the bits and bytes of it, but here I am a bit out of my comfort zone and I need to finish this by tomorrow :(
Greetings,
Kraganov
EDIT Some More tries:
rng and cell as variant:
cell = tbl.DataBodyRange(5, 1).Address(False, False)
rng = .range("A3:P59").Address(False, False)
Set test = Application.Intersect(cell, rng)
==>I would get objects required
just using rng as range and trying to set it without "set"
rng = .range("A3:P59")
EDIT 2 : I found a way around using the range.
So what I was trying to do, was the following:
I had a table that contains information about firewall rules. However, not every line describes a rule. There are also lines that described the context in which the rules below that line were to be placed.
Outside of the table, aside of those lines there would be a cell with the range of cells for that context. I wanted to use that to describe the context for those rules, if I pulled them.
I ended up looping through the table rows and identifying those specific rows and setting a "context" variable when, a row like that was met.

Try setting the cell as well as following:
set cell = tbl.DataBodyRange(5, 1).Address(False, False)
What is cell? A Range?

You do not need to add 'set' to the range value assignment.
Try just
range = .Range("A3:P59")

Function TestCellRange() As Boolean
Dim tbl As ListObject
Dim cellToTest As Range
Dim testResult As Range
Set tbl = shRulebase.ListObjects("tblBFFirewallRules")
Set cellToTest = tbl.DataBodyRange.Cells(5, 1)
'or with one more level of indirection
'Set cellToTest = shRulebase.range(tbl.DataBodyRange.Cells(5, 1).Value)
Set testResult = Application.Intersect(cellToTest, [A3:P59])
If testResult Is Nothing Then
MsgBox ("oops")
TestCellRange = False
Else
MsgBox ("yup yup")
TestCellRange = True
End If
End Function

Thanks to the post of VincentG I found the working solution. Thanks for that.
Function TestCellRange() As Boolean
Dim tbl As ListObject
Dim cellToTest As range
Dim testResult As range
Set tbl = shRulebase.ListObjects("tblBFFirewallRules")
shRulebase.Activate
Set cellToTest = tbl.DataBodyRange.Cells(5, 1)
'or with one more level of indirection
'Set cellToTest = shRulebase.range(tbl.DataBodyRange.Cells(5, 1).Value)
Set testResult = Application.Intersect(cellToTest, range("A3:P59"))
If testResult Is Nothing Then
MsgBox ("oops")
TestCellRange = False
Else
MsgBox ("yup yup")
TestCellRange = True
End If
End Function

Related

VBA code to only show rows that contain similar text to an input field?

I'm new to VBA and am trying to cobble together some code to allow a user to input a word (or several words) into a cell and then show a list of matching row entries.
I have tried the following code but am getting an "instring = type mismatch" error.
Note that "B3" is the field dedicated for the "search word" and column F is the column containing the text I want to search within. If the word is contained, I want to show that row and hide all rows that don't contain that word.
Sub Find_Possible_Task()
ROW_NUMBER = 0
SEARCH_STRING = Sheets("codeset").Range("B3")
ROW_NUMBER = ROW_NUMBER + 1
ITEM_IN_REVIEW = Sheets("codeset").Range("F:F")
If InStr(ITEM_IN_REVIEW, SEARCH_STRING) Then
Do
Cells(c.Row).EntireRow.Hidden = False
Loop Until ITEM_IN_REVIEW = ""
End If
End Sub
TIA!
Few bad coding conventions or even possibly downright errors:
It's a good practice to explicity declare the scope Public/Private of your Sub procedure
Unless you're passing the variables from some place else, they need to be declared with Dim keyword
Using Option Explicit will help you prevent aforementioned error(s)
(Subjective) variables in all caps are ugly and in most programming languages it is convention to reserve all caps variables names for constants (Const)
Option Explicit
Private Sub keep_matches()
Dim what As Range
Dim where As Range
Dim res As Range ' result
Dim lr As Long ' last active row
Dim ws As Worksheet: Set ws = Sheets("codeset")
lr = ws.Cells(ws.Rows.Count, "F").End(xlUp).Row
Set what = ws.Range("B3")
Set where = ws.Range("F1:F" & lr)
' we'll create an extra column for a loop in our .Find method
where.Copy
ws.Range("F1").EntireColumn.Insert
ws.Range("F1").PasteSpecial xlPasteValues
where.EntireRow.Hidden = True ' preemptively hide them all
Set where = ws.Range("F1:F" & lr)
Set res = where.Find(what, lookIn:=xlValues) ' ilook for matches, 1st attempt
If Not res Is Nothing Then ' if found
Do Until res Is Nothing ' repeat for all results
res.EntireRow.Hidden = False
res = "Checked"
Set res = where.FindNext(res)
Loop
Else
MsgBox("No matches were found")
where.EntireRow.Hidden = False ' we don't wanna hide anything
End If
ws.Range("F1").EntireColumn.Delete ' remove the extra help column for Find method
End Sub
Should work as expected.
If there are any question, let me know.
instead of instr(), consider range.find().
Sub Find_Possible_Task()
Dim SEARCH_STRING As String
Dim ITEM_IN_REVIEW As Range
Dim found As Range
Dim i As Integer
SEARCH_STRING = Sheets("Sheet1").Range("B3").Value
i = 1
Do
Set ITEM_IN_REVIEW = Sheets("Sheet1").Cells(i, 6)
Set found = ITEM_IN_REVIEW.Find(What:=SEARCH_STRING)
If found Is Nothing Then
ITEM_IN_REVIEW.EntireRow.Hidden = True
End If
i = i + 1
Loop Until ITEM_IN_REVIEW = ""
End Sub
alternatively, consider using filter table:
1. check if your table has filter on ==> if yes, pass. if no, turn on filter.
2. filter column F for keyword to contain value in cell B3.

Trigger automatic emails if cell in the second of two columns is empty using Excel VBA

I have column A with the dates samples are received and column I with when the sample was processed. I want Excel to send email weekly until column I is filled.
I tried making an "if then" statement but it doesn't work.
The first statement should say
"if column A is not empty and column I is empty, then send email weekly"
"if both columns are not empty, then no email needs to be sent".
I don't necessarily need the message box but I couldn't end the program without something there.
I tried to make the body of the code to then figure out the automation later.
Dim rng1 As Range
Dim rng2 As Range
Set rng1 = Range("A:A")
Set rng2 = Range("I:I")
For Each Value In rng1
If rng1.Text <> "" And rng2.Text = "" Then
Dim aOutlook As Object
Dim aEmail As Object
Dim rngeAddresses As Range, rngeCell As Range, strRecipients As String
Set aOutlook = CreateObject("Outlook.Application")
Set aEmail = aOutlook.CreateItem(0)
aEmail.Importance = 2
aEmail.Subject = "You have a message from Quarantine"
aEmail.Body = "Please complete the excel sheet for samples"
aEmail.Recipients.Add "me#email.com"
aEmail.Send
ElseIf rng1.Text <> "" And rng2.Text <> "" Then MsgBox "All projects are up to date"
ElseIf rng1.Text = "" Then
End If
Next
End Sub
When you use a For Each loop you need to make sure you're referencing the variable that you're iterating - in your case, that was Value, which you forgot to do. For the sake of your code I've replaced Value with myval - I wouldn't recommend using Value as a variable name, since it's used so much in syntax, you're going to end up confusing yourself.
No need for rng2 - just use .Offset to check the value in column I.
You also needed a way to exit your loop when you've run out of lines - otherwise you're iterating over a million rows for no reason.
I've also removed the declarations that you weren't using, and moved the other ones outside your loop to the top of the subroutine. Declare them once and be done with it.
Option Explicit
Sub Test()
Dim rng1 As Range, myval As Range, rngeAddresses As Range
Dim aOutlook As Object, aEmail As Object
Set rng1 = Range("A:A")
For Each myval In rng1
'Check column A and column I
If myval.Text <> "" And myval.Offset(0, 8).Text = "" Then
Set aOutlook = CreateObject("Outlook.Application")
Set aEmail = aOutlook.CreateItem(0)
aEmail.Importance = 2
aEmail.Subject = "You have a message from Quarantine"
aEmail.Body = "Please complete the excel sheet for samples"
aEmail.Recipients.Add "me#email.com"
'aEmail.Send
ElseIf myval.Text = "" Then
Exit For
End If
Next
End Sub

Finding a value in the range

I am writing a subroutine that looks through a range of cells starting in cell A1 (the range is 1 column wide) containing String values. My sub first finds the entire range and assign it to a Range variable "theForest" to help make searching easier. Then, it looks through each cell in the range until it finds the word “Edward”. If he is found or not, it display the result in a message (stating that he was or was not found).
The code I have so far is this:
With Range("A1")
'this will help find the entire range, since it is only one column I will search by going down
theForest = Range(.Offset(0,0), .End(xlDown)).Select
Dim cell As Range
For Each cell In theForest
If InStr(Edward) Then
Msgbox"He was found"
Else
Msgbox"He was not found sorry"
End If
Next cell
End With
However I am getting numerous errors upon running the program and I think the issue is with the
theForest = Range(.Offset(0,0), .End(xlDown.)).Select
line of code. I would appreciate any guidance into this simple code.
Thank you :)
EDIT: Here is some new code I have come up with:
Dim isFound As Boolean
isFound = False
With Range("A1")
For i = 1 to 500
If .Offset(1,0).Value = "Edward" Then
isFound = True
Exit For
End If
Next
End With
If isFound = True Then
Msgbox " Edward was found"
Else
MsgBox "Edward was not found"
End if
Then again it does not include finding the entire range and assiging it to the range variable theForest.
Dim theForest as Range, f as Range
Set theForest = ActiveSheet.Range(ActiveSheet.Range("A1"), _
ActiveSheet.Range("A1").End(xlDown))
Set f = theForest.Find("Edward", lookat:=xlWhole)
If Not f Is Nothing Then
Msgbox"He was found"
Else
Msgbox"He was not found sorry"
End If

Using .range property on string that includes sheet name

I have a string inside a cell in a workbook that I am using to define the address of a range that a lookup should be done upon.
As an example, let's say the string is called LookupRange and has the value:
''Rate Sheet'!Y111:AA126
My problem is that in my code, when I set the range I have to use:
Set yRange = ThisWorkbook.Worksheets("Rate Sheet").Range(LookupRange)
Is there a way to use the .Range() property without using the .Worksheets() property?
For instance, maybe a way to do something like:
Set yRange = ThisWorkbook.Range(LookupRange)
If not, I guess I might have to write some code to extract the sheetname from the sheet range?
Assuming LookupRange is a String, you can extract the Sheet and Range from the string using Mid() and InStr():
Sub TestIt()
Dim LookupRange As String
LookupRange = "'Rate Sheet'!Y111:AA126"
SheetL = Mid(LookupRange, 2, InStr(2, LookupRange, "'") - 2)
RangeL = Mid(LookupRange, InStr(1, LookupRange, "!") + 1, Len(LookupRange))
Set yRange = ThisWorkbook.Sheets(SheetL).Range(RangeL)
End Sub
I don't know exactly what sort of look up you are doing, but this should work..(parse it as a string)
Sub range_set()
Dim rr As Range
Set rr = Range(CStr(Range("LookupRange")))
Debug.Print Application.WorksheetFunction.VLookup(1, rr, 2, False)
End Sub
Where the named range "LookupRange" is a single cell that contains the sheet address "Sheet2!Y111:AA126" (or whatever) to be used in a "lookup".
Set yRange = Application.Range("'Rate Sheet'!Y111:AA126")

Storing Range attributes as an object?

I'm having trouble with the way I designed this little report I'm making. Is it possible to create a variable for a Range object in Excel VBA, for the purposes of applying the formatting to another Range? Here is my example:
I'm creating a dictionary from the Microsoft Scripting Runtime library:
Dim d as Scripting.Dictionary
With this I'm adding labels, values, and (trying to add) Ranges.
Dim rng as Range
rng.Font.Bold = True
d.Add 1, Field("test1", 12345, rng)
rng.Font.Bold = False
d.Add 2, Field("TestTwo", "Testing field", rng)
rng.HorizontalAlignment = xlCenter
d.Add 3, Field("threeeee", 128937912, rng)
Dim key As Variant
For Each key In d.keys
Range("A" & key).value = d(key).Label
Set Range("B" & key).value = d(key).rng
Next key
Here is my Field function:
Private Function Field(Label As String, val As Variant, rng As Range) As cField
Dim f As New cField
f.Label = Label
f.val = val
Set f.rng = rng
Set Field = f
End Function
And here is my cField class:
Option Explicit
Dim mVarValue As Variant
Dim mStrLabel As String
Dim mRng As Range
Property Let val(ByVal val As Variant)
mVarValue = val
End Property
Property Get val() As Variant
val = mVarValue
End Property
Property Let Label(ByVal val As String)
mStrLabel = val
End Property
Property Get Label() As String
Label = mStrLabel
End Property
Property Let rng(ByVal val As Range)
Set mRng = val
End Property
Property Get rng() As Range
Dim a As Range
a.value = mVarValue
Set rng = a
End Property
The idea is that the key in the dictionary is going to be the row location for the field. This way if changes need to be made to the report I'm making, the only thing that needs to be changed is the key for that particular value in the dictionary. I have been successful storing the label for the value, and the value itself, but I also want to store the formatting for that Range (bold, justification, borders, etc...).
I get a 'Run-time error '91': Object variable or With block variable not set' error on the line immediately following the rng declaration. I'm wondering if its not possible to have a generic Range that doesn't have a location on a sheet, or if somehow my syntax is off.
Any help would be greatly appreciated! :)
Is it possible to create a variable for a Range object in Excel VBA, for
the purposes of applying the
formatting to another Range?
I'm wondering if its not possible to have a generic Range that doesn't have
a location on a sheet...
The short answer is no.
The quick answer is...I suggest creating a "format" worksheet, which can be hidden or very hidden, that contains ranges, or Named Ranges, with the formatting you need. This allows you to range.Copy the "formatted" range then use range.PasteSpecial xlPasteFormats.
I dislike overwriting the user's clipboard, but it is difficult to programmatically copy the formatting of one range to another. I use this method in numerous solutions because it is flexible, maintainable, reusable, and does not rely on complex code. Moreover, I can visually change formatting without touching code.
Good question! Unfortunately, I don't think you can store a range that hasn't been initialized to an existing range of cells on your worksheet. I can think of a couple of options:
Use a hidden worksheet to store the range information
Store the range information manually, in a handful of member variables
Option 1 could be the easiest, despite the fact that it sounds like overkill to have an extra sheet kicking around. I'm picturing one hidden worksheet, defined specifically for this purpose.
Option 2 might be simplified if you only need to keep track of a couple of range properties (borders and color, for example).
You are correct - it is not possible to have a generic Range object. You have to 'Set' your range variable to some actual range to be able to read and write its properties.
But if you're "Letting" your rng property, then it seems you should already have a reference to a range. Why do you have a Property Let rng if you're not going to use that property in the Get statement.
How about this solution?
Create a class with
the range address as text, ie."$A$3:$A$11,$A$18:$A$24,$D$29".
The value
Save the formatting of the range as a format-text.
Then you could create the range by Range(RangeAdressAsText) and use something like the following
Private Sub ApplyFormatting(r As Range, ByVal f As String)
On Error GoTo ErrHandler:
f = UCase$(f)
Dim IterateRange As Range
Dim Formatarray() As String
Formatarray = Split(f, " ")
Dim i As Integer
With r
For i = LBound(Formatarray) To UBound(Formatarray)
Select Case Formatarray(i)
Case "BOLD"
.Font.Bold = True
Case "ITALIC"
.Font.Italic = True
Case "TOP"
.VerticalAlignment = xlTop
Case "BOTTOM"
.VerticalAlignment = xlBottom
Case "UNDERLINE"
.Font.Underline = True
End Select
Next i
End With
Erase Formatarray
Exit Sub
ErrHandler:
LogInformation Format(Now, "yyyy-mm-dd hh:mm:ss") & " - " & ": # ApplyFormatting in xlPrinter " & " - " & Err.Number & " - " & Err.Description & " - " & Err.Source & " - " & Err.LastDllError
End Sub