0
votes

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:

  1. Created a new excel book

  2. 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.

  3. 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
    
  4. 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"
    
  5. 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"
    
  6. 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.

1
There are known problems with .SpecialCells as they refer to single or missing range objects. A similar situation of bad returned results can be shown where the xlCellTypeVisible on the filtered range below the filter's header row with no rows showing actually returns the full filtered range down to the bottom of the worksheet, not nothing.user4039065
Thanks for the confirmations, very much!Tim, you documented the situation very well, even my feelings about it... The other interesting thing is, that I was looking for problems by google, but it didn't provide your page, which was written in times of the birth of the excel'2010... and now we are even after the excel'2013... I'm just wondering, wether is it known to MS, because its correcting would be just a little piece of code.Slow Arrow

1 Answers

1
votes

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;
    }