Channels ▼
RSS

Tech Tips


Tips

PopulateHelper: A Templated Solution for Managing Strings in Control

Recently, I worked on a project where, depending on the scenario, I either had to populate a list-box (CListBox) or a combo-box (CComboBox) control with a list of strings acquired via a database connection. Of course, I also needed the ability to extract the string at a given index within the control. I developed a class that, given an STL vector of strings, would fill a control with these strings. CListBox and CComboBox are MFC objects that both derive from CWnd, and not a super-class that offers an AddString() method, for example. Thus, if I wanted to reduce the amount of methods in my class, the traditional polymorphic manner of collapsing related functionality into a single entity was not available to me. I could have implemented overloaded forms of my methods, as shown in Listing 1.

While the code shown in Listing 1 works, this solution simply cries out for a template-based solution, as shown in Listing 2. Note that in Visual C++ .NET 2003, template member functions in nontemplate classes are now fully supported.

This is simple enough and elegant. However, recall that I also wanted a means to extract the string given an index into the control. In reading the MSDN documentation, I was dismayed to learn that, while PopulateHelper::getData() worked when specialized for a CComboBox control, it would not compile when used with a CListBox. Why, you ask? The reason involves the call to GetLBText(), which is not provided in CListBox and, hence, the compilation error; see Listing 3. It's unclear why Microsoft chose the names GetLBText() for CComboBox and GetText() for CListBox, when the input arguments for the methods are identical (as one would suspect, given the fact that the two controls are so similar in functionality).

So now my conundrum was that if I wanted to implement a truly generic utility class (generic in the sense that it worked across both CComboBox and CListBox controls), I would have to fall back on C++ overloaded methods for the latter method, due to the fact that Microsoft's interfaces were not "orthogonal," so to speak. Inspired by Andrei Alexandrescu's Modern C++ Design (Addison-Wesley, 2001), where he describes how to architect extremely useful template classes or structs he deems "policies" (see Chapter 1), I implemented PopulateHelper as shown in Listing 4.

Granted, Listing 4 might contain more code than if I had overloaded PopulateHelper::getData() on both CComboBox and CListBox. However, in the interest of clarity, one could argue that PopulateHelper::fill() should be overloaded as well. Moreover, there are numerous other instances in MFC and other third-party APIs where this lack of orthogonality can cause problems. What I've shown here is how policy-driven class design can help steer the developer around these issues by using the template support of Visual C++ .NET 2003 to its fullest extent.

Safer Node Browsing with Microsoft's XML DOM

by Matthew Wilson

matthew@synesis.com.au

When working with the Microsoft XML DOM component, I encountered a rather nasty bug. Basically, when browsing child nodes — using the node-list object returned from IXMLDOMNode::get_ childNodes() — it is sometimes useful to call the IXMLDOMNodeList::reset() method in order to ensure that any previous iteration is cancelled, and that iteration starts from the beginning of the node sequence.

However, when calling this method on a comment node (nodeName: "#comment"), it crashes somewhere (at address offset of 0x13447) inside the call to reset(). Other nodes with empty child sequences do not exhibit the same behavior.

There are a couple of workarounds. You could remember to refrain from enumerating the child nodes of comment nodes within your application code, but a much better solution is to make sure you always call the IXMLDOMNodeList::get_length() method and then only call reset() when the length is greater than 0. This is, presumably, pretty efficient, compared to calling, say, IXMLDOMNodeList::get_item() or IXMLDOMNodeList::get__newEnum() and having to release the objects returned. This technique can be codified in the following simple free function:

inline void reset_node_list(IXMLDOMNodeList *nodelist)
{
   long listLength;
   if( SUCCEEDED(nodelist->get_length(&listLength)) &&
       listLength > 0)
   {
       nodelist->reset();
   }
}

or within your wrapper class(es). In the child_node_sequence class in the XMLSTL DOM library (http://xmlstl.org/), I use the technique in the begin() method when starting a node sequence iteration.

The XML DOM component I have encountered this bug with is msxml3.dll, version 8.30.9926.0. It may or may not be present in other versions, but that is not really the point. In many deployment scenarios, you may not wish (or be able) to change the installed version of the XML DOM component, so you should code defensively and do the check.

Beware Null ListViewSubItems in .NET

by Matthew Wilson

matthew@synesis.com.au

In a recent .NET application — a simple network monitor using ICMP — I came across a potentially dangerous artifact of the behavior of the System.Windows.Forms.ListViewItem type. The ListViewItem type has a property, SubItems, that provides access to the subitems (which includes the main item itself) for the item as a collection of type System.Windows.Forms.ListViewItem.ListViewSubItemCollection. In order to add subitems to an existing item, you simply call Add() on the collection instance.

In my application, I wanted to be able to change the subitems in response to various events after the creation of the item. Further, I wanted to be able to change the order of "address" and "status" subitems at compile time by using the manifest constants ADDRESS_INDEX and STATUS_INDEX. This was to enable me to easily change the column order before the final version of the program.

Understandably, using the index operator of the collection to update a subitem resulted in a System.OutOfRangeException being thrown if the subitem did not already exist. This is the case even when you are specifying the "end" index (e.g., index 3 for an array with three items at 0, 1, and 2).

The solution to this is to create the subitems with appropriate initial values at the time of the item, and adding them to the collection, as in:

 ListViewItem item = m_items.Items.Add(host);
 ... // sometime later
 item.SubItems.Add("");
 item.SubItems.Add("");
 ... // sometime later
 subItems[ADDRESS_INDEX] = new ListViewItem.ListViewSubItem(...);
 subItems[STATUS_INDEX]  = new ListViewItem.ListViewSubItem(...);

Being a C++ kind of guy, I didn't like the idea of creating subitems — in the call to Add("") — which were going to be thrown away. So I tried changing the calls to Add():

 item.SubItems.Add((ListViewItem.ListViewSubItem)null);
 item.SubItems.Add((ListViewItem.ListViewSubItem)null);

This works fine in creating the array (within the enclosing form's constructor), but when the form is "Run()", the program terminates. The exception is, bizarrely, a System.OutOfMemoryException, though the additional information does give "Error creating window handle".

From my point of view, this is an error. The call to Add with null should throw a meaningful exception — System.NullReferenceException — rather than the program dying later in what could, in a more complex application, seem to be an unrelated manner.

Solution: Don't use null subitems!

Inserter Function Objects for Windows Controls

by Matthew Wilson

matthew@synesis.com.au

STL is receiving ever-increasing recognition in the C++ development community as more STL-compliant software — in the form of containers, algorithms, adaptors, and function objects — becomes available for our use.

As part of the WinSTL project (http://winstl.org/), I've written function objects that provide back, front, and indexed insertion for list and combo boxes.

The listbox_front_inserter is defined in Listing 5. It can be used with any sequence that contains items that can be converted to CStrings, as in:

 vector<CString> strings( . . . );
 HWND            hwndListbox = . . .;
 . . .
 for_each(strings.begin(), strings.end(),
	          winstl::listbox_front_inserter(hwndListBox));

Each item will be inserted into the beginning of the list.

The listbox_back_inserter, which inserts items at the end of the list, has the same definition, but for the details of the sent message, as in:

 ::SendMessage(m_hwndListbox, LB_ADDSTRING,
 static_cast<WPARAM>(-1), reinterpret_cast<LPARAM>(s));

The listbox_add_inserter adds items into the list according to its sorted position, and uses:

 ::SendMessage(m_hwndListbox, LB_ADDSTRING,
                 0, reinterpret_cast<LPARAM>(s));

There are corresponding function objects for working with combo boxes: combobox_front_inserter, combobox_back_inserter, and combobox_add_inserter.

Included in the source code archive is a program that demonstrates all six function objects by adding the items in the PATH environment variable to the list and combo boxes in front, back, and add forms.
w::d


George Frazier is a software engineer in the System Design and Verification group at Cadence Design Systems Inc. and has been programming for Windows since 1991. He can be reached at georgefrazier@ yahoo.com.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video