HOW TO: How do I find the last populated cell in a range?

Reference: Q0050

Article last modified on 2-Jun-2008


The information in this article applies to:

  • XLL+ for Visual Studio 2005 - 5.0
  • XLL+ for Visual Studio .NET - 4.2, 4.3.1, 5.0
  • XLL+ for Visual Studio 6 - 3, 4.1, 4.2, 4.3.1, 5.0

HOW TO: How do I find the last populated cell in a range?

Question

How can I do the equivalent of VBA's Range.End(xlDown)?

Answer

Construct a refence to the cell you want to start at, and then use the CXlRef class and CXlOper::Coerce() to iterate the cell reference until you encounter an empty cell.

The code below will inspect the cells starting with the specified cell reference and moving in the specified direction:

enum {
    xlUp,
    xlLeft,
    xlDown,
    xlRight
};

int CountPopulatedCells(const CXlOper& xloRef, int direction)
{
    if (!xloRef.IsRef())
        return -1;
    CXlOper xloCell = xloRef, xloValue;
    CXlRef xlr = xloRef.GetRef();
    xlr.Right() = xlr.Left();
    xlr.Bottom() = xlr.Top();
    xloCell.GetRef() = xlr;
    int count = 0;
    while (true)
    {
        // Is cell empty?
        if (xloValue.Coerce(xloCell) != 0)
            return -1;
        if (xloValue.IsEmpty())
            break;
        count++;
        // Move to next cell
        if (direction == xlUp)
        {
            if (xlr.Top() == 0)
                break;
            else
                xlr.Bottom() = --xlr.Top();
        }
        else if (direction == xlLeft)
        {
            if (xlr.Left() == 0)
                break;
            else
                xlr.Right() = --xlr.Left();
        }
        else if (direction == xlDown)
        {
            if (xlr.Top() == CXlRef::MaxRow())
                break;
            else
                xlr.Bottom() = ++xlr.Top();
        }
        else if (direction == xlRight)
        {
            if (xlr.Left() == CXlRef::MaxCol())
                break;
            else
                xlr.Right() = ++xlr.Left();
        }
        else
            return -1;
        xloCell.GetRef() = xlr;
    }
    return count;
}

Example

//{{XLP_SRC(RangeEndDown)
    // NOTE - the FunctionWizard will add and remove mapping code here.
    //    DO NOT EDIT what you see in these blocks of generated code!
IMPLEMENT_XLLFN2(RangeEndDown, "R", "RangeEndDown", "", 
    "User Defined", "Returns the number of populated cells in"
    " the range starting at the active cell and moving down", "",
    "appscope=1\0", 2)

extern "C" __declspec( dllexport )
LPXLOPER RangeEndDown()
{
    XLL_FIX_STATE;
    CXlOper xloResult;
//}}XLP_SRC

    CXlOper xloRef;
    if (!xloRef.GetActiveCell())
        CXllApp::XlMessageBox("Not a cell reference", XlMessageBoxTypeInformation);
    else
    {
        int count = CountPopulatedCells(xloRef, xlDown);
        char msg[128];
        _snprintf(msg, sizeof(msg), "%d cells are populated", count);
        CXllApp::XlMessageBox(msg, XlMessageBoxTypeInformation);
    }
    return xloResult.Ret();
}

See also

CXlOper::IsEmpty() in the online documentation.
CXlOper::GetRef() in the online documentation.
CXlRef class in the online documentation.