Automatically Size CListView Columns

Apr 12, 2008
This article was written back when Visual Studio 6 was new. As such, I cannot guarantee that the content of this article still applies in modern Windows environments.

The CListView class (a simple variant of the CListCtrl class) is a useful way to provide a resizable, list control application. But you may have noticed that the columns in "report" mode do not resize themselves when the user resizes the program window. So how can we make the columns fit nicely in the list view, without the need for a horizontal scroll bar? The answer is simpler than you may have thought!

Sizing the Columns

By overriding the WM_SIZE event for the view class, we can handle sizing the columns neatly. The following example will have four columns total, two of which will be proportional to the window size, and two of which will be a fixed size (no matter how large the window gets). Here is the code:

void CPaperplusView::OnSize(UINT nType, int cx, int cy) 
{
    CListView::OnSize(nType, cx, cy);

    if(m_List->GetSafeHwnd() != NULL)
    {
        // Get the list control size
        CRect r;
        m_List->GetClientRect(&r);

        // Figure out how large the column interval should be
        int scrollwidth = GetSystemMetrics(SM_CXVSCROLL);
        int listwidth = r.Width() - scrollwidth - 80 - 80;
        int col_interval = listwidth / 10;

        m_List->SetColumnWidth(0, col_interval * 6);
        m_List->SetColumnWidth(1, col_interval * 4);
        m_List->SetColumnWidth(2, 80);
        m_List->SetColumnWidth(3, 80);

        m_List->ShowScrollBar(SB_HORZ, FALSE);
    }
}

The first method call that appears in this code block was inserted by ClassWizard and is simply a call to the base class (to allow the normal processing to get handled). I then make sure that the pointer to my list control (m_List in the above code block) is not an illegal value. This prevents some hairy problems when the program is first starting up.

Understanding the Code

First, I get the client size of the list control and store it in a CRect variable. The next call is to the GetSystemMetrics() function, into which I pass the SM_CXVSCROLL parameter. This method call is used to get the width of the Windows vertical scroll bar. We need to provide space for this scroll bar so that the columns will fit nicely when the vertical scroll bar does indeed show up. I next determine how much space I have left over to work with. After subtracting out the scroll bar width, as well as the two fixed size columns (both of which are 80 pixels wide), I have the size available to the first two, proportional columns. I then divide that remaining space into 10 parts, for use with the variable-sized columns.

Next, I actually set the column widths. You will note that the first column will take up 60% of the available space (excluding the vertical scroll bar and fixed-size columns). Likewise, the second column will take up the remaining 40% of "free" space. After those two columns are dealt with, I make sure that the last two columns are sized appropriately (to their fixed values of 80 pixels).

The final step is to make sure that the horizontal scroll bar never shows up. I'm not 100% sure if we only need to make this call one time, but I figure that it doesn't hurt to turn off the horizontal scroll bar every time a size event gets called. Why is this a necessary line of code if we are going to great pains to size things correctly? Well, for some strange reason, when a window is horizontally sized smaller than its initial value, the scroll bar still shows up, even though there is nothing that is scrolled off screen to either the left or right! There is definitely a strange bug at work here, so I took this means of 'fixing' it.

You may notice that even though we have done a great deal of work to make things fit nicely, there will occasionally be some left over space in the list control header. This space always shows between the vertical scroll bar and the last column. Why isn't this space used like it should be? The simple answer is that we are doing integer division when dividing up the list size into "available" space. So, that little bit that was truncated from that integer division problem constitutes for this remainder. The easiest fix for this is to simply add the "left over" pixel width to the final column. Then your columns will always fit perfectly!

So there you have it. Dynamic column sizing is a quick addition to any program that utilizes the CListView and is sure to make your program look that much more professional.

No comments (yet!)

Leave a Comment

Ignore this field:
Never displayed
Leave this blank:
Optional; will not be indexed
Ignore this field:
Both Markdown and a limited set of HTML tags are supported
Leave this empty: