Reputation: 1
To summarize: if the base range of the SpecialCells is just one cell, then the result of range.SpecialCells(xlCellTypeConstants, xlNumbers) yields not just that cell or nothing. I supposed, SpecialCells should yield a range inside that object it is applied... or am I wrong?
I've "developed" a little new excel book, just to show what I don't understand, as follows:
Created a new excel book
Filled the A1:B3, as follows:
A B ...
1 1 12
2 2 22
3 3 32
.
All the other cells of the sheet are untouched.
Inside a new module I've added the following code:
Private Sub test()
Dim oSet As Range, oSpec As Range, oUsed As Range
Worksheets("Sheet1").Activate
Set oSet = ActiveSheet.Range("A1:A1")
Set oSpec = oSet.SpecialCells(xlCellTypeConstants, xlNumbers)
Set oUsed = ActiveSheet.UsedRange
Set oSet = Nothing
Set oSpec = Nothing
Set oUsed = Nothing
End Sub
Running the subroutine, and stopping it at the first range reset, it yields:
oSet.Address = "$A$1"
oSpec.Address = "$A$1:$B$3" ' this seems to be wrong; should it be just "$A$1" ?
oUsed.Address = "$A$1:$B$3"
Having been changed the value of A1 to A, rerun the sub, and stop at the same place, it gives (consistently with the previous, which shows it works consequtively):
oSet.Address = "$A$1"
oSpec.Address = "$B$1","$A$2:$B$3" ' this seems to be wrong; should oSpec be nothing ?
oUsed.Address = "$A$1:$B$3"
However, resetting the value of A1 to the original 1, but changing the range of the first Set operation in the Subroutine from just the cell "A1" to a true range of cells "A1:A2", then rerun the sub, and stop at the place, it gives very different (and more expected) result:
oSet.Address = "$A$1:$A$2"
oSpec.Address = "$A$1:$A$2" ' this is good
oUsed.Address = "$A$1:$B$3"
I would appreciate, if somebody would explain the results. Thanks ahead.
Upvotes: 0
Views: 2057
Reputation: 659
This is happening because in case of one cell, it considers UsedRange instead.
References:
1) Super secret SpecialCells
2) Using SpecialCells in Excel VBA
As a helpful note, I will give you my wrapper function for SpecialCells and its helper method (in C#, not in VBA):
/// <summary>
/// <para>Wrapper for SpecialCells function.</para>
/// </summary>
/// <param name="inputRange"></param>
/// <param name="cellType"></param>
/// <returns></returns>
/// <remarks>Throws null when there are no cells in <paramref name="inputRange"/> corresponding to <paramref name="cellType"/>, unlike SpecialCells which throws exception</remarks>
public static Range GetRangeSpecialCells(this Microsoft.Office.Interop.Excel.Range inputRange, XlCellType cellType)
{
try
{
if (inputRange.Cells.Count == 1)
{
if (cellType == XlCellType.xlCellTypeComments)
{
if (inputRange.Comment != null)
{
return inputRange;
}
else
{
return null;
}
}
else if (cellType == XlCellType.xlCellTypeFormulas)
{
if (inputRange.HasFormula == true)
{
return inputRange;
}
else
{
return null;
}
}
else if (cellType == XlCellType.xlCellTypeBlanks)
{
if (string.IsNullOrEmpty(inputRange.Value2) == true)
{
return inputRange;
}
else
{
return null;
}
}
else if (cellType == XlCellType.xlCellTypeLastCell)
{
return inputRange;
}
else
{
// since inputRange has a single cell, SpecialCells will apply to entire worksheet
// this range has all cells from worksheet (UsedRange) of type provided: cellType
Range temp = inputRange.SpecialCells(cellType);
// intersect range with single cell (inputRange) with above range
Range rangeOverlap = Intersect(inputRange, temp);
// if range with single cell is contained in that intersection, then this cell is of type xlCellTypeConstants
if (rangeOverlap.Count == inputRange.Count && rangeOverlap.Rows.Count == inputRange.Rows.Count && rangeOverlap.Columns.Count == inputRange.Columns.Count)
{
return inputRange;
}
else
{
return null;
}
}
}
else
{
return inputRange.SpecialCells(cellType);
}
}
catch (System.Runtime.InteropServices.COMException ex)
{
return null;
}
}
/// <summary>
/// <para>Customized function for intersection of two ranges (<paramref name="rangeA"/> ∩ <paramref name="rangeB"/>)</para>
/// </summary>
/// <param name="rangeA"></param>
/// <param name="rangeB"></param>
/// <returns>Range corresponding to intersection of the two provided ranges</returns>
/// <remarks>This function returns null if any of provided ranges is null or malformed, unlike Application.Intersect which throws exception</remarks>
public static Range Intersect(Range rangeA, Range rangeB)
{
Range rngIntersect;
if (rangeA == null)
{
rngIntersect = null;
}
else if (rangeB == null)
{
rngIntersect = null;
}
else if (rangeA.Worksheet != rangeB.Worksheet)
{
rngIntersect = null;
}
else
{
try
{
rngIntersect = Globals.ThisAddIn.Application.Intersect(rangeA, rangeB);
}
catch (Exception ex)
{
rngIntersect = null;
}
}
return rngIntersect;
}
Upvotes: 1