Implementing Alphanumeric Ordering

Copyright 2017 by David Wincelberg

Revised Article Source Code

I made some changes to the source code described in my October 2000 Dr. Dobb's Journal article in response to suggestions from a reader. Click for the revised source code. This source code, the sorting-controls source and the STL source may be used in any program, including commercial ones. Please credit me in your program's help file or documentation.

MFC Sorting Controls (Visual Studio 2013, Visual C++ 6.0)

In the following image, the list boxes show the differences between alphabetic, simple-alphanumeric and full-alphanumeric ordering. Running SortTest will show these differences also in owner-draw list boxes, check list boxes, combo boxes, list controls and tree controls. On the Data page, you can edit the list of strings to sort, save the list to a file and load it from a file. In addition, you can insert, add or delete individual strings to or from a page's controls.

SortTest: shows different sort orders in various controls


Item File Name Size API Docs Notes
SortTest SortTest.exe 93 KB   Demo program
Simple-alphanumeric ordering 2 KB StringOrder Sort controls default to alphabetic ordering.
Sort list box 11 KB SortListBox Includes adjusting horizontal extent.
Sort list box owner draw 14 KB SortListBoxOD Includes adjusting horizontal extent.
Sort check list box 11 KB SortCheckListBox Includes adjusting horizontal extent.
Sort combo box 8 KB SortComboBox  
Sort list control 9 KB SortListCtrl  
Sort tree control 8 KB SortTreeCtrl  
All of the above 93 KB    

Release Notes

Each of these six sorting-controls has two associated classes: an order-class and a data-class. By default, these controls sort alphabetically. By subclassing the associated order-class and connecting it with SetSortOrder(), you can specify custom ordering. For example, in my CopyFolder program, the Size column of the Preview list control sorts based on file sizes instead of displayed strings.

The order classes allow for replacing the default string-order, without subclassing these classes, by calling SetStringOrder(). This function and SetSortOrder() contain an optional parameter named bHandleDelete. Being able to swap-in a sort order or a string order and specify whether to delete it later relates to the Strategy and Flyweight design patterns. Since these sort-controls don't need to be subclassed to change sort or string orders, the design principle of "encapsulate what varies" applies.

These functions can only be called before any strings have been added to a sort-control and after the content of this control has been cleared.

The data classes support the features of the related controls. They can be subclassed to contain additional data. Each of the AddString(), InsertString() and InsertItem() methods has an optional parameter for passing a data object of the appropriate class. Alternatively, you can use SetItemData() to store application-specific data for all of these sorting-controls. The data classes contain a field for holding the supplied data. Also, SetItemDataPtr() can be used for the list box, list box owner-draw, check list box and combo box sorting-controls. The corresponding retrieval methods are GetItemData() and GetItemDataPtr().

Calling InsertString() places a string in a specified position in a control. Strings that are inserted with this function are ignored when sorting strings that are added with AddString(). The various sort list-box and combo-box controls support this functionality.

The various sort list-box controls handle horizontal-extent adjustments.

See the comments at the top of each sorting-control file or the API documentation for required and optional window styles. In debug mode, the sort-classes assert if required styles are absent or prohibited styles are present.

To support using both of the above development environments, I placed the _T() macro around character and string contents, and used generic character or string functions. As a result, ASCII and Unicode are supported. I also noticed memory leaks in the list-box and combo-box classes when using VS 2013 that did not occur when using VC++ 6.0. I fixed this by adding a method that is called when a window for these classes is destroyed. In addition, memory is released when ResetContent() is called.

I also noticed that CWinApp::Enable3dControls() is no longer needed and that StaticLink must be modified. For the latter, see this fix.


To incorporate CSortListBox into a dialog box or a property page, put the following lines into the the dialog box's or property page's .h file.

#include "SortListBox.h"   // near the top of the file
CSortListBox m_myListBox;   // among the attributes

In the corresponding .cpp file, put the following line into OnInitDialog() before this control is referenced.

m_myListBox.SubclassDlgItem (IDC_MY_LIST, this);

Be sure to set the window styles as indicated in SortListBox.cpp or the corresponding API documentation. Note that sorting can be turned off by removing the sort style.

If you prefer, you can use ClassWizard to put in a variable of type CListBox and then change it to CSortListBox. You will still need the above include statement but won't need to subclass this variable.

The instructions for incorporating the other controls into an application are similar.

back to top

Text Alphanumeric Sorting

As a further test of the alphanumeric-ordering classes, I wrote a program (TextANSort.exe, 103 KB) to sort lines of text alphanumerically. The results from this program can be saved to a file.

Text Alphanumeric Sorting program

back to top

Visual Studio 2017 ANO Updates

I recompiled SortTest and TextANSort with Visual Studio 2017.  No code changes were needed.  When I first loaded the associated solution files, I saw the following window and clicked on the OK button (shown here for SortTest).

Review Solution Actions Window

Here are the new files: SortTest-VS17.exe (94 KB) and TextANSort-VS17.exe (106 KB).

back to top

Alphanumeric Ordering with STL (4 KB) contains a sample program that implements simple alphanumeric ordering in an STL multiset. SANOCompare.cpp implements less-than in operator() (const std::string s1, const std::string s2).

If you uncomment the first line of this function and comment out the second one, it will call the revised source code by using std::string::c_str() to retrieve a constant C string.

If you leave operator() as it is, it will call a version of SimpleANCompare that operates on C++ strings by using iterators. Since atoi does not operate on standard strings or iterators, another function (stoi) is implemented in SANOCompare.cpp. The standard implementation of stoi is not useful here since it starts at the beginning of a standard string and may throw exceptions.

ANOwithSTL.cpp contains the following lines:

std::multiset<std::string, CSANOCompare>::iterator iter;
for (iter = strSet.begin(); iter != strSet.end(); ++iter) {

This could be simplified by using the auto keyword.

for (auto iter = strSet.begin(); iter != strSet.end(); ++iter) {

back to top

david dot wincelberg at gmail dot com
(In the above line, change "at" and "dot" to their symbols and remove the spaces to produce my e-mail address.)

Return to main page
Last updated: 6-April-2017