|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
The problemSeveral times now I've found the need for a pair of list boxes that are linked. Items can appear in either of the list boxes, but not in both. Items can be moved between list boxes, and have processes performed on them. Their order within the list boxes can often be shuffled.
The first time I ended up with all of the code to do this spread throughout the message handlers of the enclosing dialog box. Not very neat, but it worked. Then I needed to do something very similar and found myself reinventing the wheel, cutting and pasting from one dialog box to another. I stopped and pulled all of the code out and placed it where it should be, in classes especially designed to solve my problem. The result was a set of classes that can be plugged into any dialog box with minimum fuss and do all of the work required for linked list boxes, and much more besides.
Just a pair of list boxes?As I thought about the problem I realised that the include/exclude control was just a special case of a pair of the kind of extended list box that I am always inventing. A list box with buttons closely associated with it. The buttons allow the user to perform processes on the selected items in the list box, or move items into, or out of the box. The buttons are often only enabled when items are selected or when there are a certain number of items or more in the list box. Where only one button is associated with the list box the action that the button performs can often also be achieved by double clicking the item.
A better list box?An extended list box that does just what we want can be created as a subclass ofCListBox. We can introduce the concept of a
"list box item processor", an abstract class which provides an interface for performing
processing on a list box item. A processor can then be associated with the list box so
that double click handling can be performed inside the list box rather than outside of
it. By associating a list of buttons with the list box, the buttons can have their
enabled/disabled state managed by it. When items are added or removed the state of all
associated buttons can be automatically adjusted. Where the buttons are required to
perform an action on a selected item, when pressed, we can associate a list box item
processor with them to allow them to process the item.
There are some peculiarities with the standard
We can use message reflection so that all of the code for
our extended list box exists inside our The end result is a list box that makes it easier to do most of the "standard" things that you might want to do with a list box. All the code is in one place, so it's easy to put it in a library and use it in lots of dialog boxes. Associated buttons are managed automatically by the list box, and as a user you just need to derive a class from the list box item processor to manipulate the items when they are double clicked or when an associated button is pressed.
The classes involvedThe following class diagram shows the classes involved in building our extended list box:
To take advantage of the extended functionality of
The only other class that a user of the list box must be aware of is
class CJBListBox::ItemProcessor { public : // Destruction... virtual ~ItemProcessor() { } // Process an item virtual void ProcessSelectedItem( const int nIndex, const CString &theString, void *pData, PostProcessAction_e &action) = 0; };
If multiple items are selected then each will be passed in turn to the list processor. This is possibly a weakness of the design, but I have yet to come across a situation where I needed to know about all of the items in a multi-selection before I could process any of them.
Known problems with item manipulationAt present the code doesn't handle a multiple selection move up or down which retains the items as selected after the move. This is because I can't find a way to build an extended selection programatically. Also I don't expect multiple selections where some items are moved and some deleted to work. I haven't tried too hard to fix these as I don't require the functionality - if you do require it, and you do fix up the code to handle it, please let me know!
Other related classesDue to the member functions supplied by our extended list box it's easy use list item processors to rearrange the order of items within the list box, or simply remove items. These kinds of item processors are just "simple item processors" since all they do is return an action to be performed. Since they're handy to have I provided a template simple item processor class. To use just instantiate with the action you wish to perform, e.g.
CJBListBox::TSimpleItemProcessor<CJBListBox::MoveDown> downProcessor; CJBListBox::TSimpleItemProcessor<CJBListBox::Delete> deleteProcessor;
To nest, or not to nest...All of the classes involved are contained in a namespace. Initially I thought that was good enough, but I realized that many of the classes were merely implementation details of other classes and weren't for external use. I'd already placed the declaration for the list node class inside the list itself. I followed this practice to its logical conclusion by placing the button inside the list, and the list and item processor inside theCJBListBox class. I've yet to decide if this has added
complexity, or whether it was necessary, and I had to jump through a few hoops to keep
class wizard happy.
Nesting the classes has added a tighter level of coupling to the classes. In one way this is good because the classes that are coupled are implementation details of the classes they are coupled to. However, I dislike the way that more source files are dependant on each other.
Building the include/exclude controlOnce we have built this extended list box solving our original problem is easy. We just take two of our extended list boxes, write list item processors that removes an item from one box and inserts it into the other, wire up a few buttons, and we're done.
The classes involvedOnce we have the extended list box, the rest is easy...
The decision to make the two list boxes public data members was based on the fact that if we didn't make them publicly accessible then forwarding functions for each of the useful member functions would need to be written. These forwarding functions would also need to be able to determine which of the two boxes they should be applied to. This seemed unnecessary.
The list swap item processor was relatively easy to
create. The processor needs to be given a list box to add items to. When its
See the article on Len's homepage for the latest updates.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||