Hacker’s Guide to Visual FoxPro
An irreverent look at how Visual FoxPro really works. Tells you the inside scoop on every command, function, property, event and method of Visual FoxPro.
These methods let you populate and depopulate combo boxes and list boxes whose RowSourceType is set to 0 (for None) or 1 (for Value). The two Add methods add new items to the combo or list. The Remove methods take individual items out of the list. The Clear Method
removes all items from the list. Understanding the differences between the two pairs of add and remove commands could drive a person to drink. Luckily, Tamar’s a teetotaler.
oObject.AddItem( cValue [, nIndex [, nColumn ] ] )
oObject.AddListItem( cValue [, nItemId [, nColumn ] ] )
oObject.Clear()
oObject.RemoveItem( nIndex )
oObject.RemoveListItem( nItemId )
Parameter |
Value |
Meaning |
cValue |
Character |
The text of the item to be added to the list. |
nIndex |
Numeric |
The position in the list of the item to be added or removed. |
nItemId |
Numeric |
The unique identification number of the item to be added or removed. |
nColumn |
Numeric |
The column in which the newly added item is to be placed. |
Let’s start with Clear. It’s simple and does what it’s supposed to. It removes all the items from a combo or list box. That’s it. When you want to make a new start, call Clear.
Now on to the confusing stuff. The key to understanding the difference between the “item” and the “listitem” or “itemid” methods (not just Add and Remove, but a bunch of others, too) is to understand that Microsoft thought they were doing us a favor (and they were) in providing the two different numbering systems used for elements of a list or combo box.
The first numbering system is based on the “what you see is what you get” school of thinking. Each item is assigned a number based on its physical position in the list. This is called the item’s ListIndex (or, more briefly, Index). As new items are added or old items are removed, an item’s ListIndex can change. Changing the Sorted property also can change an item’s ListIndex. So can moving items around with the mover bars. Fundamentally, a ListIndex is a fleeting thing.
The other numbering scheme is more permanent. When an item is added, it’s assigned a unique ID number called ItemId. That item keeps that ItemId as long as it’s in the list. No other item in the list will ever have that ItemId.
That’s probably enough for you to distinguish between RemoveItem and RemoveListItem. RemoveItem accepts an Index and deletes the item that currently has that Index. RemoveListItem takes an ItemId and deletes the item that has (now and forever) that ItemId. (Actually, not quite forever. After you delete the item with a particular ItemId, you can add a new one with the same ItemId.) Reasonably straightforward, once you know the secret.
Unfortunately, adding items is even more complex. When the list or combo has only one column, it’s not too bad, though. In that case, AddItem with no nIndex parameter inserts a new item. If Sorted is .F., it goes at the end of the list and simply gets the next Index (and, of course, a new unique ItemId). If Sorted is .T., it gets inserted at the appropriate location and is assigned the appropriate Index for that location. The Indexes of all the items after the new one go up by one, in the appropriate order.
Still with us? Okay, what if you pass a value for nIndex? If Sorted is .T., the value you pass is ignored and the new item goes into the correct alphabetical position. That’s easy enough. If Sorted is .F., the new item is inserted at the position you passed and all the items following it move down one (their Indexes increase by 1).
The key fact to remember about AddItem is that it always adds a new row to the list or combo. Accepting this now will make it easier to understand the discussion about multi-column lists and combos below.
On to AddListItem. This method has a different point of view, but the simplest case is pretty much the same as the simplest case of AddItem. If you don’t pass a value for nItemId, the new item is assigned a unique ID number and is either added at the end of the list or, if Sorted is .T., goes into its rightful position.
What if you do pass a value? It depends whether an item already exists with that ItemId. If not, a new item is added to the list and its ItemId is the one you passed. However, if there’s already an item with that ItemId, its text changes to cValue. In other words, AddListItem doesn’t always add a new item to the list—sometimes, it just changes an existing item. Again, keep this in mind as we tackle multi-column lists.
Take a deep breath. Here we go. When ColumnCount is more than 1, each item in the list has multiple columns. This is where AddItem and AddListItem really part company. AddItem is incapable of populating a multi-column list or combo box with data in more than one column for each row. Go back three paragraphs to see the reason why—AddItem always adds a new row.
What does this mean in practice? First, that the nColumn parameter of AddItem, while not meaningless (it does do what it says it does), is worthless. We have yet to want a list or combo where each row contains data in only one column.
Second, and more important, it means that the right way to fill a multi-column list or combo box is to use AddListItem. The unfortunate part of this is it means you have to keep track of which ItemIds have been used so you don’t accidentally overwrite some data. Take a look at NewItemId—it’s a big help in this process.
To fill one row of a multi-column list, you pass the same value of ItemId for each column and the appropriate value for nColumn for each data item.
We should note that you can also populate a list or combo by simply putting the desired data in the List or ListItem array property. In fact, you can avoid AddItem and AddListItem entirely if you want, unless you need to assign particular ItemIds. In that case, your best bet is probably to use AddListItem for the first column, then assign directly to ListItem for other columns.
A final note. We were very surprised to find that these methods work with RowSourceType = 1. We’d figured that type was pretty brain-dead—meant mostly for those cases where the list of items was truly fixed. Help is silent on whether this is meant to work or not, so don’t assume that everything that works for RowSourceType=0 also works for RowSourceType=1. Also, be aware that an undocumented capability like this may just disappear down the road. All in all, our take is that you should just stick with RowSourceType 0 if you think the list may need to be changed, and save type 1 for really, really fixed items.
* Fill a list box with the names and titles from Employee
* This code might be in the Init method of the list box
This.ColumnCount = 3
This.ColumnWidths = "75,60,150"
LOCAL nRow
SELECT Employee
nRow=1 && See NewItemId for a better way
SCAN
This.AddListItem(Last_Name, nRow, 1)
This.AddListItem(First_Name, nRow, 2)
This.AddListItem(Title, nRow, 3)
nRow=nRow+1
ENDSCAN
* To see them in alpha order, add
This.Sorted=.T.
ComboBox, List Property, ListBox, ListCount, ListIndex, ListItem, ListItemId, NewIndex, NewItemId