Class with full row highlighting, dragable headers, sorting

Information above the divider pertains to the update of 6/13/98. I have redone the sorting. It should be much improved now. I have tested this sorting, and it will work much more reliably than the old stuff. Also I added support for sorting by float. There is now no need to check the Owner Draw Fixed box in the dialog editor. ListCtrlEx will automatically set LVS_OWNERDRAWFIXED if row hilighting is enabled.


I needed to add some functionality to my list controls, so of course, I came to the Code Guru for help. I found several very good articles on extending the CListCtrl class, but none that included all of the features I needed. By combining the code from three of these articles and adding some features of my own, I have created an easy to use extended list control class. CListCtrlEx extends CListCtrl by adding full row highlighting, column header drag and drop, and column sorting. Columns can be sorted by string(case or no case), number, or date/time. All, none or any combination of these features can be enabled.

I would like to thank the authors of the following articles:
Selection highlighting of entire row
Dragging columns to rearrange column sequence

Download Source (CListCtrlEx.h, and CListCtrlEx.cpp) 8Kb

To use CListCtrlEx:

1. Add the header to your dialog box header file or StdAfx.h:
#include "CListCtrlEx.h" 

2. Add a CListCtrlEx to your dialog box class where you would add CListCtrl:
CListCtrlEx m_List; 

3. Initialize the list control in the dialog box OnInitDialog function to enable the features you want:

BOOL CListDlg::OnInitDialog() 
{
	CDialog::OnInitDialog();
		
	//set the column types and enable sorting
	if(m_List.SetColumnTypes(_gnLogColTypes, NUM_LOGCOLUMNS))
		m_List.EnableSort();	
	//set the type of row highlighting
	m_List.SetHighlightType(HIGHLIGHT_ROW);
	//enable drag and drop headers
	m_List.EnableHeaderDrag();
	//set the base time for sorting on a 24 hour clock
	m_List.SetBaseTime("21:04:00");
	return TRUE;  
}

Notes:

_gnLogColTypes and NUM_LOGCOLUMNS are globally defined as follows:

#define NUM_LOGCOLUMNS	4
static unsigned char _gnLogColTypes[NUM_LOGCOLUMNS] = 
{
	LVEX_NUM, LVEX_CASE, LVEX_TIME, LVEX_NOCASE
};

If you do not call SetColumnTypes() but you enable sorting, all columns will be sorted as case sensitive strings. The sorting types are defined as:

LVEX_NUM: Sort as an integer number
LVEX_CASE: Sort as a string (case sensitive)
LVEX_NOCASE: Sort as a string (not case sensitive)
LVEX_TIME: Sort as a date/time
LVEX_NOSORT: Do not sort this column
LVEX_NUM_FLOAT: Sort by floating point number
LVEX_TIME_BASE: Sort as 24 hour time with a starting time (see below, will probably never use)

The row highlighting types are defined as:

HIGHLIGHT_NORMAL: Highlight the first column only (default CListCtrl behavior)
HIGHLIGHT_ALLCOLUMNS: Highlight all columns but not unused space
HIGHLIGHT_ROW: Highlight the entire row including unused space

I added the base clock time feature to fit a very specific need I had. Most people will probably not have to use it. The records I display commonly have 24 hour clock times (no date) beginning in early evening and running to morning (ie: 23:00:00 to 05:00:00) and I needed to sort the time from the starting time (23:00:00) not by the lowest time. If you do not call SetBaseTime(), the date/time will be sorted by absolute time.



Comments

  • Drag image in DragColumn

    Posted by Legacy on 10/15/2003 12:00am

    Originally posted by: Torrie

    In the DragColumn fonction, only text is moved by the function SetItemText.
    How can I move the image attached by a Cimagelist with a subitem ???
    Please help !

    Reply
  • Great Job!

    Posted by Legacy on 03/15/2003 12:00am

    Originally posted by: Kingwood

    Thank you very much!
    My program work very well when I used this Class!

    Reply
  • Improved column insertion

    Posted by Legacy on 06/11/2002 12:00am

    Originally posted by: Martin

    Simple change to the insertion location so that the insertion point moves
    
    to the next column half way along a column.

    Added: h1,h2 variables for half col1 half col2.


    void CHeaderCtrlEx::OnMouseMove(UINT nFlags, CPoint point)
    {
    if(m_bDragEnable) {
    if( (MK_LBUTTON & nFlags) == 0) {
    // The left mouse button is not pressed - so reset flags
    m_bCheckForDrag = FALSE;
    m_bDragging = FALSE;
    }
    else if( m_bDragging ) {
    // Get column number that falls under the mouse
    int i=0, cx = 0, h1,h2;
    if( point.x > 0 )
    for( i = 0; i < GetItemCount(); i++ )
    {
    if(i == 0)
    h1 = 0;
    else if(m_pWidth[i-1] == 0)
    h1 = 0;
    else
    h1 = m_pWidth[i-1] / 2;

    if(m_pWidth[i] == 0)
    h2 = 0;
    else
    h2 = m_pWidth[i] / 2;

    if( point.x >= (cx - h1) && point.x < (cx + h2) )
    break;

    cx += m_pWidth[i];
    }
    if( i != m_nDropPos ) {
    m_nDropPos = i;
    CRect rect;
    GetWindowRect( &rect );
    // Invalidate area occupied by previous marker
    InvalidateRect( &marker_rect );

    // Draw a new marker
    CClientDC dc(this);
    POINT pts[3];
    pts[0].x = cx;
    pts[1].x = cx - rect.Height()/4;
    pts[2].x = cx + rect.Height()/4;
    pts[0].y = rect.Height();
    pts[1].y = pts[2].y = rect.Height()/2;
    dc.Polygon( pts, 3 );

    // save marker information
    marker_rect.left = cx - (rect.Height()/4 + 1);
    marker_rect.top = rect.Height() - (rect.Height()/2 + 1);
    marker_rect.right = cx + (rect.Height()/4 + 1);
    marker_rect.bottom = rect.Height() + 1;
    }
    return;
    }
    else if( m_bCheckForDrag ) {
    // The mouse button was pressed over a column header
    // and now the mouse has moved - so start drag
    m_bCheckForDrag = FALSE;
    m_bDragging = TRUE;
    m_nDropPos = m_nDragCol;
    SetCapture();

    // Store information for later use
    int iCount = GetItemCount();
    HD_ITEM hd_item;
    m_pWidth = new int[iCount];
    for( int i = 0; i < iCount; i++ ) {
    hd_item.mask = HDI_WIDTH;
    GetItem( i, &hd_item );
    m_pWidth[i] = hd_item.cxy;
    }
    return;
    }
    }

    CHeaderCtrl::OnMouseMove(nFlags, point);
    }

    Reply
  • Painting glitch

    Posted by Legacy on 04/15/2002 12:00am

    Originally posted by: Brendan

    If you highlight a row, and then scroll the view left and right using the horizontal scrollbar, the painting gets messed up.

    In particular, the vertical line of alternating dots - that is normally just part of the surrounding selection rectangle - gets drawn repeatedly along the highlighted bar.

    Any idea how to fix this?

    Reply
  • Simple way to add the control in your dialog

    Posted by Legacy on 10/27/2001 12:00am

    Originally posted by: Igor Zavoichinsky

    The simplest way is:
    1) Add ListCtrl on the dialog in the resource editor
    2) Assign variable via ClassWizard
    3) Insert #include "ListCtrlExt.h" into dialog header file
    3) Correct variable type declaration in dialog header file from:
    CListCtrl m_ServerList
    to
    CListCtrlEx m_ServerList

    Reply
  • Colum type error fixed

    Posted by Legacy on 11/09/2000 12:00am

    Originally posted by: a.m.sharfudeen

    The authors effort is praise worthy ,but  i wonder who he failed  to notice the type of colum change when the user drags the item
    
    since sorting is performed by colum type and it is likely to get changed while dragging, we have to restructure the type array in the "ondragcolumn "function

    here is the code that does it

    unsigned char srctype=m_pTypes[source];
    unsigned char desttype=m_pTypes[dest];

    // the authors code
    unsigned char *newtypes=new unsigned char[m_nNumCols];


    for(int ind=0,x=0,j=0;ind<m_nNumCols;ind++)
    {

    if(x==dest)
    {
    newtypes[x]=srctype;
    x++;
    continue;
    }
    if(j==source)
    {
    j++;
    continue;

    }
    newtypes[x]=m_pTypes[j];
    x++;
    j++;
    }

    // note the above code should be iunserted before incrementing the source

    // here the author increments the source since insering
    increases the column by one

    unsigned char *cachepointer=m_pTypes;
    m_pTypes=newtypes;
    delete cachepointer;


    // the authors code

    //

    Reply
  • If LVN_ITEMCHANGED is handled

    Posted by Legacy on 06/29/1999 12:00am

    Originally posted by: Paul Rosen

    I made one addition to the class, which was otherwise very
    
    easy to drop in and use. I handle the LVN_ITEMCHANGED
    notification so that I can put up a preview whenever the
    user selects something in the CListCtrl. It turns out that
    this class causes A LOT of these notifications, so my
    application was putting up hundreds of previews during
    sorting and when a column was dragged to the first position.

    I made the following modification:


    In CListCtrlEx() class definition:

    public:
    bool ShouldIgnoreNotifications() { return m_bShouldIgnoreNotifications; };
    private:
    bool m_bShouldIgnoreNotifications;

    In the cpp file:

    CListCtrlEx::CListCtrlEx()
    {
    ...
    m_bShouldIgnoreNotifications = false;
    }

    void CListCtrlEx::OnHeaderClicked(NMHDR* pNMHDR, LRESULT* pResult)
    {
    HD_NOTIFY *phdn = (HD_NOTIFY *) pNMHDR;

    if(phdn->iButton == 0 && m_bSort)// Button is the left mouse button
    {
    m_bShouldIgnoreNotifications = true;

    ...

    m_bShouldIgnoreNotifications = false;
    }
    *pResult = 0;
    }

    void CListCtrlEx::DragColumn(int source, int dest)
    {
    ...

    // Moving a col to position 0 is a special case
    m_bShouldIgnoreNotifications = true;

    ...

    m_bShouldIgnoreNotifications = false;

    Invalidate();
    }

    Then, in my app,

    void MyView::OnItemchangedList(NMHDR* pNMHDR, LRESULT* pResult)
    {
    if (!m_cList.ShouldIgnoreNotifications())
    {
    // do preview
    }

    *pResult = 0;
    }

    Reply
  • Good! Some small problems

    Posted by Legacy on 02/12/1999 12:00am

    Originally posted by: Patrick M. Powers

    For compilation need to include 
    
    #include <afxdisp.h> //For OLE date/time
    in ListCtrlEx.h
    and
    #include "ListCtrl.h"
    in ListCtrlEx.cpp.

    Drag and drop of columns is OK but types of the columns do
    not move with the columns, so they may become incorrect.
    I have a fix for this.

    As an obscure difficulty
    there was trouble with implementing handling of
    column header clicking from outsize the class. Oven though
    column sorting was disabled the outside code didn't get the messages.
    I finally commented out
    //ON_NOTIFY(HDN_ITEMCLICKA, 0, OnHeaderClicked)
    //ON_NOTIFY(HDN_ITEMCLICKW, 0, OnHeaderClicked)
    which did the trick. I tried using ON_NOTIFY_EX here
    but still did not get messages in my code.

    Reply
  • CListCtrlEx

    Posted by Legacy on 12/01/1998 12:00am

    Originally posted by: Brian Kowald

    In the instructions, on how to use in a dialog add:

    m_List.SubclassDlgItem(IDC_LIST, this);


    Step 1 of the instructions should read

    #include "ListCtrlEx.h"
    instead of
    #include "CListCtrlEx.h"


    The implementation file listctrlex.cpp does not included the header. Add
    #include "ListCtrlEx.h"
    to it.

    Works great!

    Brian

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: October 29, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you interested in building a cognitive application using the power of IBM Watson? Need a platform that provides speed and ease for rapidly deploying this application? Join Chris Madison, Watson Solution Architect, as he walks through the process of building a Watson powered application on IBM Bluemix. Chris will talk about the new Watson Services just released on IBM bluemix, but more importantly he will do a step by step cognitive …

  • It's no secret what keeps CIOs up at night. Mobile, cloud, data, security, and social have become the "five imperatives," the drivers of business progress, innovation, and competitive differentiation. Business leaders around the world want to hear how other companies are succeeding. How are they applying the latest technologies? How did they get started? What outcomes are they achieving? Read this online magazine for success stories from organizations like the NBA, Pfizer, and San Jose State University as they …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds