Function to check if a range is contained in another - vba

I am looking to create a function that checks if a cell (Range1) is contained within Range2. The function would be:
Function IsWithin(Range1 as Range, Range2 as Range) as Boolean
This is meant to go in a Before_DoubleClick event to check that the cell clicked belongs to a range.
Examples of expected input/output (using addresses directly only to make it easier to imagine):
IsWithin("A2", "A1:B3") = True
IsWithin("B1","B1:B2") = True
IsWithin("A3", "A4:C10") = False
IsWithin("A3", "A3") = True
Off the top of my head I can think of a simple way to do this:
Function IsWithin(Range1 as Range, Range2 as Range) as Boolean
Dim cell2 as range
For each cell2 in Range2
If cell2.address = Range1.Address then
IsWithin = True
Exit Function
End if
Next
End function
Now for the harder part, and the question. If I am selecting a Merged cell that protrudes inside Range2, I'd like it to count as being part of the range (even if some of the merged cell sticks out). What would I need to write to get that done?
Example considering A1:B3 is a merged cell (Still sending addresses instead of range objects as a way to represent it easier):
IsWithin("A1:B3", "A2:D7") = True

Is there a reason why you're not using Intersect()?
Dim r As Range
Set r = Intersect(Range("A2"), Range("A1:B3"))
If r Is Nothing Then
Debug.Print "Not in range"
ElseIf r.Address = Range("A2").Address Then
Debug.Print "Completely within range"
Else
Debug.Print "Partially within range"
End If
Edit:
As #Bacon mentioned in the comments, this doesn't work with merged cells. But you can use the MergeArea property to test for that. Assuming A1:B1 is a merged range, this should work:
Set r = Intersect(Range("A1").MergeArea, Range("B1:B3"))
If r Is Nothing Then
Debug.Print "Not in range"
Else
Debug.Print "Ranges intersect"
End If
MergeArea returns the merged range, if it's part of a merged area. If not, it just returns the single cell. So you should be safe always using MergeArea for the source when you test the intersection, as shown above in the edit.

Related

Am I using the Columns() property improperly, and if so, is there a good workaround?

I'm currently in the process of solving a
type mismatch error
in a macro I'm writing, and I've written a short subroutine to drill down on the specific issue. This subroutine should loop through all of Column A, entering the numbers 1-10 in rows 1-10.
Sub looptest()
Dim rRange As Range
Dim rCell As Range
Dim i As Integer
Set rRange = ThisWorkbook.Worksheets(1).Columns(1)
i = 0
For Each rCell In rRange
If i < 10 Then
i = i + 1
rCell.Value2 = i
End If
Next rCell
End Sub
Instead this fills every cell in Column A with 1. Stepping through it in debug mode shows that instead of referencing a single cell, rCell references the entire column.
I have found that if I replace
Set rRange = ThisWorkbook.Worksheets(1).Columns(1)
with
Set rRange = ThisWorkbook.Worksheets(1).Range("A1:A100")
the macro works as intended, however I'd prefer to be able to use Columns() or something similar in my production code.
Am I using the Columns() property improperly, and if so, is there a good workaround?
Begin with these changes:
Set rRange = ThisWorkbook.Worksheets(1).Columns(1).Cells
Dim i As Long
Using the .Cells lets us loop over the cells in a column rather than columns in a worksheet.

VBA set cell value after .ClearContents

Why does the cell value not set in another function when clearing the contents of the range in another function?
I'm trying to set a cell value to "All" after clearing the cells in the range. I've even tried to get a message box to pop up to see if i can somehow check if my check value is correct.
DelRange is the range i'm clearing.
Building is the cell that i'm checking the value for and if it's blank, it needs to change to "All".
clearPreviw is used to clear another sheet, which it's doing.
Sub ClearSheet()
Dim Dash As Worksheet
Dim DelRange As Range
Dim Building As Range
Set Dash = ActiveWorkbook.Worksheets("DASH")
Set DelRange = Dash.Range("FilterData")
Set Building = Dash.Range("SelBuild")
DelRange.ClearContents
Call clearPreview
'This part below doesn't work when the Range.ClearContents has been done, but doing it on it's own without clearing the range works fine
If Building.Value = "" Then
MsgBox "Building is empty", vbOKOnly
Building.Value = "All"
End If
End Sub
I've run this test as a separate process which works, but once again when running it as a call function right after .ClearContents seems to stop this.
Sub test()
Dim Dash As Worksheet
Dim DelRange As Range
Dim Building As Range
Set Dash = ActiveWorkbook.Worksheets("DASH")
Set DelRange = Dash.Range("FilterData")
Set Building = Dash.Range("SelBuild")
If Building.Value = "" Then
MsgBox "Building is empty", vbOKOnly
Building.Value = "All"
End If
End Sub
I've been poking at it and searching but i can't wrap my head around this.
I think you are missing:
Building.ClearContents;
Also I would prefer:
If IsEmpty(Building.Value) Then
over:
If Building.Value = "" Then
This link gives you a good start on how to set range variables (although I would advice you against the use of .Select and .Activate).
After that, use .ClearContents or .Clear, depending on your needs.
If you properly cleared the ranges, there is no need to check if they are empty, so this might be a redundant step within your current planning.

How to loop through range names and hide rows if cell = 0?

I have several VBA routines in an Excel 2007. There is a template worksheet which gets copied (and accordingly altered) up to 50 times. Now, this template contains a range called "HideRows", so this range gets copied several times in all those new worksheets. I want to hide all rows that contain the value 0 in the range "HideRows". Not all rows shall be hidden, only those rows that contain the value 0. This is what I've got so far:
Option Explicit
Sub HideEmptyRows()
Dim rngName As Range
Dim cell As Range
Application.ScreenUpdating = False
For Each rngName In ActiveWorkbook.Names
If rngName.Name = "HideRows" Then
With cell
For Each cell In rngName
If .Value = 0 Then
.EntireRow.Hidden = True
End If
Next cell
End With
End If
Next rngName
What's wrong here and what do I need to do to get it to work?
You can address the named range directly without looping. There is no test that this named range exists, as per your description it is safe to assume so.
Secondly, do not use the "with" statement outside of the loop that sets the referenced variable. Try this instead:
Option Explicit
Sub HideEmptyRows()
Dim rngName As Range
Dim cell As Range
Application.ScreenUpdating = False
For Each cell In range("HideRows")
If cell.Value = 0 Then
cell.EntireRow.Hidden = True
End If
Next cell
Application.ScreenUpdating = True
edit:
If the workbook contains multiple identical sheets where each sheet may contain this named range you will have to loop. This code will not loop over all names but over all sheets, and test for existance of the named range in each sheet:
Sub HideEmptyRows()
Dim sh As Sheets
Dim rng As Range, cell As Range
For Each sh In ActiveWorkbook.Sheets
Set rng = Nothing ' this is crucial!
On Error Resume Next
Set rng = sh.Names("HideRows")
On Error GoTo 0
If Not rng Is Nothing Then
For Each cell In rng
cell.EntireRow.Hidden = (cell.Value = 0)
Next cell
End If
Next sh
End Sub
The range variable has to be reset explicitly before the assignment as this step is skipped if the range does not exist. The following If would use the value last assigned then, which would be wrong.

Excel VBA. For each row in range, for each cell in row, for each character in cell

I have created a VBA macro simple to absurdity.
For each row in range, then for each cell in row and then for each character in cell.
But it returns property or method not supported.
Now, to refer to each character in a cell in Excel VBA, it has to be defined as a range. So how is not a cell in a row in a range not a range object?
In all Excel examples the cells with Characters property have been referred to in absolute values. Like Worksheets("Sheet1").Range("A1")
Do I have to write a code of
Worksheets("MySheet").Range("B1")
...
Worksheets("MySheet").Range("B2")
...
...
Worksheets("MySheet").Range("B250")
To access characters or is there a way to access each character in a cell without Mid function?
By the way, Mid function looses formatting but I am looking for underlined characters.
My program is like this
Sub Celltest()
For Each rw In Sheets("Joblist").Range("B1:D250").Rows
For Each cel In rw.Cells
For Each char In cel.Characters
If char.Font.Underline = True Then MsgBox char
Next
Next
Next
End Sub
As a result I get message that a property or method is not supported.
Thank you!
David
"Why is suddenly a part of a range object suddenly not a range?" That's not the issue. The error is Object doesn't support this property or method. That means that (basically) anything after the . is an object, so the error is telling you that something is up with how you're using .Character.
I did some quick searching around, and this works:
Sub Celltest2()
Dim rw As Range, cel As Range
Dim i As Integer
Dim char
For Each rw In Sheets("Joblist").Range("B1:D250").Rows
For Each cel In rw.Cells
For i = 1 To Len(cel)
'Debug.Print cel.Characters(i, 1).Text
If cel.Characters(i, 1).Font.Underline = 2 Then
MsgBox (cel.Characters(i, 1).Text)
End If
Next i
Next
Next
End Sub
I just looked up the Characters Object documentation, and there is the answer of how to use .Characters. The issue is how .Characters is used with the range - no sophisticated skills or knowledge necessary. Just use the VB Error messages.
Two things I noticed. 1. Characters is not a collection, so For Each won't work. 2. Font.Underline is not Boolean. Try something like:
Sub Celltest()
Dim rw As Excel.Range
Dim cel As Excel.Range
Dim char As Excel.Characters
Dim I As Long
For Each rw In Sheets("Joblist").Range("B1:D250").Rows
For Each cel In rw.Cells
For I = 1 To cel.Characters.Count
Set char = cel.Characters(I, 1)
If char.Font.Underline <> xlUnderlineStyleNone Then MsgBox char.Text
Next I
Next
Next
Set rw = Nothing
Set cel = Nothing
Set char = Nothing
End Sub
Hope that helps

VBA: How to display a cell value by its defined name?

I have already a defined name for a particular column in my worksheet.
How to display a cell value by its defined name?
I've tried these:
Public Sub Test()
Dim R As Range
Set R = ThisWorkbook.Names("SomeName").RefersToRange
MsgBox CStr(R.Value)
End Sub
but run-time error occured "Type Mismatch" (error code: 13).
What's wrong with the code?
What is the actual data type of RefersToRange.Value?
The documentation says that RefersToRange returns the Range object, but it seems differ with the Range object returned by ActiveCell, because I've no problem with the following code:
MsgBox CStr(ActiveCell.Value)
I've been using Excel 2003
RefersToRange does return a Range object. I assume you're getting your Type Mismatch on the Cstr line. If the range has multiple cells, the Value property returns a Variant array. Do this in the Immediate Window (Control+G in the VBE).
?typename(thisworkbook.Names("SomeRange").RefersTorange)
Range
?typename(thisworkbook.Names("SomeRange").RefersToRange.Value)
Variant()
The CStr function can't handle an array argument and that's why you're getting that error. You can get to a particular cell in the range, like the first cell, like this
ThisWorkbook.Names("SomeRange").RefersToRange(1).Value
Or loop through them as Fink suggests.
You Need to iterate through the cells within that range and get the value of each cell separately if the range spans multiple cells. Otherwise it throws an error.
Sub Test()
Dim r As Range
Dim cell As Range
Set r = Range("Something")
If r.Cells.Count > 1 Then
For Each cell In r.Cells
MsgBox cell.Value
Next cell
Else
MsgBox r.Value
End If
End Sub
However, you can set the value to all of the cells defined in the range by setting the value of a multi-cell range like this:
Sub Test()
Dim r As Range
Set r = Range("Something")
r.Value = "Test"
End Sub
You should be able to just supply the name to the Range (or even the Cells) property:
Set R = ThisWorkbook.Range("SomeName")
You can get cell value by its name as follows:-
Workbook.Range("SomeName").Value