CListCtrl, sooner or later you will find the allure of custom draw list controls too great to resist. When that happens, you will find yourself drawing each item - and each of its subitems - one at a time, in order to achieve the stunning visual UI that your app deserves.And sooner or later, you will stumble across
CListCtrl::GetSubItemRect(), in your quest to add special fonts, colors, and other effects. This is not an API you want to tackle when you are sleep-deprived or otherwise under the weather. Unlike every other CListCtrl API, GetSubItemRect() is one-based. What does this mean? It means that for subitems 1...N, it works fine. For subitem 0, what you get is the rect for the entire row, not just the first column. It might take you a while to reach this understanding, since bizarre visual funnies are normal when you are trying to pimp a UI. Finally you narrow it down, and discover the dark side of CListCtrl::GetSubItemRect().When I first discovered this, I remember thinking, Why on earth did they do it that way? Couldn't they just do another API - something like
GetTheWholeDamnRowRect()? Well, OK, I actually thought some other things, too, but let's get back to GetSubItemRect(). The obvious thing to try is to use a little arithmetic on the adjoining subitems, but there are several gotchas with this approach; for starters, what if subitem #2 (that's "2" using one-based nomenclature) has a width of zero (that's "0" using... well, you get it). A nice, simple solution suddenly becomes a bunch of nested if's marching across the screen. (I know, I've tried it, don't go there).But where else can we get the dimensions of the first subitem? In this case, the header control is our friend. We use
CListCtrl::GetSubItemRect() to get the top and bottom borders of the rect, and then use CHeaderCtrl::GetItemRect() (which thankfully is zero-based!) to get the left and right borders.The code in a derived class looks like:
BOOL CMyListCtrl::GetSubItemRect(int nItem,
int nSubItem,
int nArea,
CRect& rect)
{
// get rect from CListCtrl (top and bottom borders)
BOOL ok = CListCtrl::GetSubItemRect(nItem, nSubItem, nArea, rect);
// if nSubItem == 0, the rect returned by CListCtrl::GetSubItemRect
// is the entire row, so we use left and right values from header
if (ok)
{
CRect rectHeaderItem;
VERIFY(m_HeaderCtrl.GetItemRect(nSubItem, &rectHeaderItem));
rect.left = rectHeaderItem.left;
rect.right = rectHeaderItem.right;
}
return ok;
}

