Indicating sort order in header control

The list view control does not provide any visual feedback on whether the list is sorted. To give feedback to the users of our application, we can use the owner draw feature of the header control and display a triangle pointing downwards or pointing updwards, indicating whether the list is sorted in the ascending or the descending order. This, of course, is applicable only if the list view control is in the report view mode.

Since we have to use an owner drawn header control, we will need to derive a class from CHeaderCtrl and add the functionality in this class. Here are the steps involved.

Step 1: Derive class from CHeaderCtrl

If you don't have a class derived from CHeaderCtrl, derive one now. You can use the Class Wizard to create one for you. I used the name CMyHeaderCtrl for the derived class.

Step 2: Add member variables

Add member variables in CMyHeaderCtrl to track the column that is sorted and the sorting order. These variables are declared as protected members and we will provide a function to set them.

protected:
int m_nSortCol;
BOOL m_bSortAsc;

Step 3: Initialize the variables in the constructor

Initialize the m_nSortCol variable to -1 in the constructor. This indicates that the list is not sorted.

CMyHeaderCtrl:: CMyHeaderCtrl()
{
m_nSortCol = -1;
}

Step 4: Add function SetSortImage()

Add a function SetSortImage() to the CMyHeaderCtrl class. This is the function that will be used to set the sort indicator. The SetSortImage() function takes the column number as an argument and also a boolean value to indicate whether it is sorted in the ascending order or in the descending order.

After setting the internal variables, the function set the header item to owner drawn. This will ensure that the DrawItem() function will get called. The function then invalidates the header control so that any previous sort indicator is removed and the new one is displayed.

Step 5: Override DrawItem()

The DrawItem() is where the sort indicator actually gets drawn. Besides drawing the sort triangle, this function is now also responsible for drawing the column label itself. The DrawItem() function is called for each item in the header control that has the HDF_OWNERDRAW format.

These are the step we take in the DrawItem() function to draw the column label and the triangular image to indicate the sort order:

Attach the device context handle passed in through the argument to a CDC object for easier device context handling. The handle is detached from the CDC object before the function returns. If we did not detach the handle then the DC would be released when the CDC object is destroyed.

We save the DC and change the clipping region so that all the updates are contrained within the header item for which the DrawItem() function is called. The device context is restored before the function returns.

We compute the offset used when drawing the label and the sort triangle. The offset is used to leave a margin around the label and is equal to twice the width of a space character.

We determine the format to be used when drawing the column label. Since the column label can be aligned left, center or right, we have to choose an appropriate format for the DrawText() function. You will also notice the flag DT_END_ELLIPSIS. This tells the DrawText() function that if the text doesn't fit with the rectangle specified, then the text should be shortened and three dots appended to the text so that the result fits within the rectangle.

We next adjust the rectangle within which the label will be drawn and then draw the lable using DrawText().

Finally we draw the triangle to indicate the sort order. We use two different color to draw the triangle so that it matches the other GUI elements in Widnows. The COLOR_3DHILIGHT color is used for edges facing the light source, and the COLOR_3DSHADOW color is used for the shadow.

Step 6: Add member variable for header control in list view class

Now that we are done with the CMyHeaderCtrl class, we have to add a member to the CListCtrl or the CListView derived class so that we can access the extended functionality. Add a protected member.

protected:
CMyHeaderCtrl m_headerctrl;

Step 7: Subclass the header control

We have to sub-class the header control so that the DrawItem() function in CMyHeaderCtrl can get called. If you are using a CListView derived class, you can place the sub-classing code in OnInitialUpdate(). If you are using a CListCtrl derived class, then put the code in PreSubclassWindow(). In either case, make sure you call the base class version of the function before subclassing the header control.
If the listview control was not created in the report view mode, then you have to change the style of control before trying the subclass the control. You can use ModifyStyle() for this. The reason why we need to change the style to the report view mode is that the header control is created only when the control first taken to the report view mode.

Step 8: Use SetSortImage() to indicate sort order

Now you are all set to add the sort order indicator. Whenever you sort the list view control, call the CMyHeaderCtrl::SetSortImage() function with the column number on which the list is sorted and the order of sorting. E.g.

Top White Papers and Webcasts

Live Event Date: March 19, 2015 @ 1:00 p.m. ET / 10:00 a.m. PT
The 2015 Enterprise Mobile Application Survey asked 250 mobility professionals what their biggest mobile challenges are, how many employees they are equipping with mobile apps, and their methods for driving value with mobility.
Join Dan Woods, Editor and CTO of CITO Research, and Alan Murray, SVP of Products at Apperian, as they break down the results of this survey and discuss how enterprises are using mobile application management and private …

On-demand Event
Event Date: February 12, 2015
The evolution of systems engineering with the SysML modeling language has resulted in improved requirements specification, better architectural definition, and better hand-off to downstream engineering. Agile methods have proven successful in the software domain, but how can these methods be applied to systems engineering? Check out this webcast and join Bruce Powel Douglass, author of Real-Time Agility, as he discusses how agile methods have had a tremendous …