RectControl → Control → Object

ListBox Class

The scrollable ListBox control, used to display one or more columns of information. You can add a checkbox and/or a picture to a row.

Events
CellAction | Change | HeaderPressed
CellBackgroundPaint | CollapseRow | KeyDown
CellClick | CompareRows | LostFocus
CellGotFocus | DoubleClick | MouseDown
CellKeyDown | DragReorderRows | MouseDrag
CellLostFocus | DragRow | MouseUp
CellTextChange | ExpandRow | SortColumn
CellTextPaint | GotFocus |

Properties
ActiveCell | ColumnType | List
AutoHideScrollBars | ColumnWidths | ListCount
Bold | DataField | ListIndex
CellAlignment | DataSource | RequiresSelection
CellAlignmentOffset | DefaultRowHeight | ScrollBarHorizontal
CellBorderBottom | EnableDrag | ScrollBarVertical
CellBorderLeft | EnableDragReorder | ScrollPosition
CellBorderRight | Expanded | ScrollPositionX
CellBorderTop | GridLinesHorizontal | SelCount
CellCheck | GridLInesVertical | Selected
CellTag | HasHeading | SelectionType
CellType | Heading | SortedColumn
Column | HeadingIndex | Text
ColumnAlignment | Hierarchical | TextFont
ColumnAlignmentOffset | InitialValue | TextSize
ColumnCount | Italic | Underline
ColumnSortDirection | LastIndex | UseFocusRing

Methods
AddFolder | ColumnValueProvider | RemoveRow
AddRow | DeleteAllRows | RowFromXY
Cell | EditCell | RowPicture
CellBold | InsertFolder | SetFocus
CellItalic | InsertRow | Sort
CellUnderline | InvalidateCell |
ColumnFromXY | PressHeader |

# Notes

You can control font style on a cell-by-cell basis and set the column alignment. With some programming, you can create hierarchical lists that use disclosure triangles to show nested items.

Because this is a RectControl, see the RectControl for other properties and events that are common to all RectControl objects.

Items in single-column ListBoxes can be accessed using the List property. The List property is an array. Arrays are zero-based which means that the first row of the List property of a ListBox is row number 0 (zero).

## Multi-Column ListBoxes

You can create multi-column ListBoxes by changing the ColumnCount property. The first column in a multi-column ListBox is column 0 (zero). This means that the ColumnCount property will always be one more than the number of the last column. The maximum number of visible columns is 64 (columns 0 through 63). You should set ColumnCount to the number of columns that you want to display. If you want to put data in an invisible column, set the column width to zero.

You can use the InitialValue property to set up the inital values of multi-column ListBoxes by separating the column values with tabs and row values with carriage returns.

The widths of columns in multi-column ListBoxes can be set by passing the widths as a list of values separated by commas to the ColumnWidths property. The widths can be passed in pixels or as percentages of the total width of the ListBox. If you don't pass widths for all the columns, the remaining columns will be evenly spaced over the remaining space. If too many widths are passed, the additional values are ignored. If the total of the widths passed is greater than the width of the ListBox, then the remaining columns will be truncated.

Specific cells in a multi-column ListBox can be accessed using the Cell method.

## Decimal Alignment

When you use decimal alignment in a cell or column, you must take into account the fact that REALbasic aligns the decimal separator with the right edge of the column or cell. You must pass a negative number to CellAlignmentOffset or ColumnAlignmentOffset to make room for the numbers to the right of the decimal place. The correct value to pass depends on the number of digits to the right of the decimal place in the column or cell.

## Hierarchical ListBoxes

Creating a simple hierarchical ListBox is more involved than a two-column ListBox because you must manage hiding and displaying the sublist data. A simple way to do this is to assign the sublists to a "hidden" column in the ListBox and toggle the display of that data when the user double-clicks on a "parent" element.

You create a row with a disclosure triangle using the AddFolder method (rather than the AddRow method) and then set the Hierarchical property to True. See the Example for a simple hierarchical ListBox with one level.

## Resizing Columns

There are two "modes" for column resizing. There is no formal mode property. Rather, the "mode" is implicitly set according to whether every column width is specified as an absolute amount. If you specify all columns either in pixels or as a percentage, you will be using the second mode. If you use an asterisk or leave a column width blank, you will be using the first mode.

A change to one column width affects the width of another column.

If column i gets bigger, column i+1 gets smaller by the same amount. This mode is great when using a ListBox without a horizontal scrollbar. You turn this mode on when you have at least one column width that is blank, or specified using an asterisk (e.g. "", " ", "*", or "4*")

NOTE: By design you can't resize the right edge of the last column in this mode. To resize the last column you need to resize the previous column.

Each column width is independent and can grow or shrink on its own.

You are responsible when the user does this, and you need to provide a horizontal scrollbar so that the user can get to the any headers that have been pushed out of view to the right. You enable this mode by making sure every column width is specified in terms of an absolute pixel width, or a percentage width (e.g. "20", or "35%"). If you use an asterisk or leave a column width blank, you will automatically be using the first mode.

You can switch between mode 1 and 2 at runtime using the same criteria as above.

The ColumnWidths property (the only one that can be set in the IDE), is equivalent to the concatenation of all of the ColumnWidthExpressions.

ColumnWidthExpressions are strings and they can represent several different types of column width calculations: absolute pixels (e.g., "45"), percentages (e.g. "22.3%"), and asterisk widths (or blanks) (e.g. " ", "4*")

ColumnWidthExpressions retain their type even when a column is resized. This means that if you:

Resize a window to which a ListBox is locked, it will grow or shrink. The columns grow or shrink as well if their expressions were *-based (unless you use "0*"), or percentage based (0%). If you want them to stay fixed, you need to express the ColumnWidthExpression as an absolute pixel value.

If you resize a column by dragging it internally, it will recompute its percentage or asterisk value. This is so that you can, say, start with a two-column ListBox with no column widths specified (each column will take up half the space). Then drag one column to take up 3/4 of the space, then enlarge the ListBox, and now both column widths will enlarge so that their widths remain in a 3/4 to 1/4 ratio.

Changing the pixel value of a column will not change its fundamental type, but will change the value of that type.

Finally, if you want to create columns that won't get resized, change the UserResizable property for each of the columns in question. If you are using mode 1, you will need to change the UserResizable property for both the column and the one to its left.

## Databases

A ListBox is often used to display the results of Database queries. A ListBox can be bound to a DatabaseQuery control so that it displays the results of the SQL query passed to the DatabaseQuery control automatically. See the example of object binding in the chapter "Creating Databases with REALbasic" in the User's Guide. Otherwise, a ListBox can be populated with the results of a query programmatically. See the example for DatabaseField.

## Scrolling a ListBox Horizontally

The ScrollBarHorizontal property automatically adds a horizontal scrollbar to the ListBox. The ScrollPositionX property can be used to get the position of the thumb or scroll the ListBox horizontally. This is the easiest way to scroll a ListBox.

You can also use the ScrollPositionX property in conjunction with a ScrollBar control to scroll contents of the ListBox horizontally. This ListBox has nine columns, eight of which have a width of 100 pixels and one has a width of 50 pixels.

The ScrollBar control has the following code in its Open event handler:

Me.maximum=500
Me.minimum=0
Me.lineStep=50

The values for maximum and linestep were chosen to match the total width of the ListBox's columns. Its ValueChanged event handler has the following line of code:

ListBox1.scrollPositionX= Me.value

## Customized Scroll Controls

If you want miniature scrollbars or scrollbars that leave room for status bars or buttons, set ScrollBarVertical and/or ScrollBarHorizontal to False and use separate ScrollBar controls in conjunction with whatever other controls that you want to locate in the a portion of the area normally taken up by standard scrollbars. Use the ScrollPosition and ScrollPositionX properties to control scrolling, as described in the preceding section.

# Examples

Adding a row to ListBox1:

ListBox1.addrow "October"

Inserting a row at row 1 in ListBox1:

ListBox1.insertRow 1, "October"

Changing all items in the ListBox to bold, underline:

ListBox1.bold= True
ListBox1.underline= True

Copying the fifth element of ListBox1 to another variable:

Dim e as String
e=ListBox1.list(4)

Adding a column to ListBox1 and setting the widths of the columns to 50 and 65 pixels, respectively:

ListBox1.columnCount=2
ListBox1.columnWidths="50,65"

Setting the number of columns of ListBox1 to three and setting the widths of the columns to 60%, 20% and 20% respectively:

ListBox1.columnCount=3
ListBox1.columnWidths="60%,20%,20%"

If ListBox1 is 100 pixels wide and has three columns, the following code will set the columns widths as indicated but the last column will only be 10 pixels wide instead of 20:

ListBox1.columnWidths="60,30,20"

If ListBox1 is 100 pixels wide and has three columns, the following code will set the columns widths but the last column will not be displayed:

ListBox1.columnWidths="60,40,20"

Copying the fifth row of the third column of ListBox1 to another variable:

Dim e as String
e=ListBox1.cell(4,2)

Assigning a value to the fifth row of the third column of ListBox1:

ListBox1.cell(4,2)="Bill"

Setting the fifth row of the third column of ListBox1 to bold, italic:

ListBox1.cellBold(4,2)= True
ListBox1.cellItalic(4,2)= True

Setting the picture for the fifth row of ListBox1 to "MyFolderPicture":

ListBox1.RowPicture(4)=MyFolderPicture

Setting up the DragRow event handler to allow the user to drag a value from a ListBox:

Function DragRow(Drag as DragItem, Row as Integer) as Boolean
Drag.Text=ListBox1.List(Row)
Return True

Summing the numeric values of the selected rows:

Dim i, total as Integer
For i=0 to ListBox1.ListCount-1
If ListBox1.Selected(i) then
total=total+ Val(ListBox1.List(i))
End if
Next

This example expands the first row of ListBox1 (if it is collapsed) or collapses it (if it was expanded). The row must have been added with the AddFolder method:

ListBox1.Expanded(1)= Not ListBox1.Expanded(1)

This example populates a three-column ListBox with headings:

ListBox1.heading(0)="ID"
ListBox1.heading(1)="JobTitle"
ListBox1.heading(2)="Name"

This example sets up a ListBox with four visible columns plus one hidden column. Column zero is hidden:

Me.columncount=5
Me.columnwidths="0,25%,25%,25%,25%"
Me.heading(0)="ID"
Me.heading(1)="FirstName"
Me.heading(2)="LastName"
Me.heading(3)="Phone"
Me.heading(4)="Zip"

The following line of code displays the value of the hidden column in the selected row:

MsgBox ListBox1.Cell(ListBox1.ListIndex,0)

## Hierarchical ListBoxes

The following example creates a single-level hierarchical ListBox.

Windows uses plus and minus signs to indicate disclosure; Macintosh and Linux use disclosure triangles. To display these widgets, set the set the Hierarchical property of the ListBox to True in the Properties pane.

The following code, which is in the ListBox's Open event handler, populates the hierarchy: The s1 string contains the parent level and sub1 contains the elements that are nested within each of s1's elements. It is a list of comma-delimited lists, with each list delimited by semicolons.The elements of sub1 are initially hidden because they are stored in a hidden column.

Dim i, u as Integer
Dim s1,sub1 as String
Me.columnwidths="150,0"
s1="Michigan,Ohio,Minnesota"
sub1="Grand Blanc,Bad Axe,Flint,Benton Harbor, Detroit;Cleveland,Columbus,Akron, Pleasantville; St. Paul,Frostbite Falls"
u= CountFields(s1,",")
For i=1 to u
If NthField(sub1,";",i)<> "" then
Me.addfolder ""
Me.cell(i-1,1)= NthField(sub1,";",i)
end if
Me.cell(i-1,0)= NthField(s1,",",i)
Next
Me.ColumnCount=1

Note that the AddFolder method, rather than AddRow, is used to add the State names.

The following line of code in the DoubleClick event handler toggles the expanded state of the row that was double-clicked:

Me.expanded( Me.listindex)= Not Me.expanded( Me.listindex)

The following code in the ExpandRow event handler runs when the user double-clicks a collapsed element:

Dim s1 as String
Dim i,u as Integer
s1=me.cell(row,1)
u= CountFields(s1,",")
For i=1 to u
Me.addrow ""
Me.cell(me.lastIndex,0)= NthField(s1,",",i)
Next

It creates the sublist rows each time the user double-clicks a collapsed state name.

The following code is in the CollapseRow event handler:

Dim i,u,NSubRows as Integer
NSubRows= CountFields( Me.cell(row,1),",")
u=row+1
For i=row+NSubRows downto u
Me.removerow i
Next

It removes the rows that were created by the ExpandRow event handler.

## Drag and Drop between ListBoxes

The following example allows the user to drag one row from ListBox1 to ListBox2. ListBox1 has its EnableDrag property set to True and its SelectionType property set to zero (Single). Its DragRow event handler is as follows:

Function DragRow (Drag as DragItem, Row as Integer) as Boolean
drag.text= Me.List(row)
Return True //allow the drag

ListBox2's Open event handler has the line:

Me.AcceptTextDrop

Its DropObject event handler is this:

Sub DropObject (obj as DragItem)
Me.AddRow obj.text //adds the dropped text as a new row

The following example allows the user to drag more than one row from ListBox1 to ListBox2. The dragged rows are added to the end of the list.

ListBox1 has its EnableDrag property set to True, enabling items in its list to be dragged, and its SelectionType property set to 1 (Multiple row selection). Its DragRow event handler is as follows:

Function DragRow (Drag as DragItem, Row as Integer) as Boolean
Dim nRows, i as Integer
nRows= Me.ListCount-1
For i=0 to nRows
If Me.Selected(i) then
Drag.AddItem(0,0,20,4)
Drag.Text= Me.List(i) //get text
End if
Next
Return True //allow the drag

It uses the AddItem method of the DragItem to add an additional item to the DragItem each selected row. The DropObject event handler then cycles through all items to retrieve all dragged rows.

ListBox2 has the following line of code in its Open event handler. It permits it to receive dragged text.

Me.AcceptTextDrop

Its DropObject event handler checks to see if the dragged object is text; if it is, it adds a row to the end of the list and assigns the text property of the dragged object to the new row: It loops through all items in the DragItem until NextItem returns False.

Sub DropObject(obj as DragItem)
Do
If Obj.TextAvailable then
Me.AddRow(Obj.Text)
End if
Loop until Not obj.NextItem

You can also drag from ListBox1 to the desktop to get a text clipping or to another application that supports text drag and drop.

## Colors

This example, which is placed in the CellBackgroundPaint event, assigns alternating colors to the rows in a ListBox:

If row Mod 2=0 then
g.foreColor= RGB(158,238,255)
else
g.foreColor= RGB(172,255,145)
end if
g.FillRect 0,0,g.width,g.height

Notes: The CellBackgroundPaint event passes the parameters g ( Graphics), and the row and column numbers (as Integer). You can assign a color to the ForeColor property by creating it as a constant in the App class or a module and assign the color constant to the ForeColor property.

The following line in the CellTextPaint event tells REALbasic to draw the text in the preceding example in red:

g.foreColor= RGB(255,0,0)

The CellTextPaint event is passed the coordinates of the suggested starting position to draw text in the parameters x and y. You can use them in a call to the DrawString property to specify the string to draw in a particular cell:

If row=4 and column=1 then
g.foreColor= RGB(255,0,0)
g.DrawString "Payment Overdue!",x,y
end if
Return True

## Sorts

To sort a ListBox, set the column on which the ListBox will be sorted with the SortedColumn property. Specify the sort direction on that column with the ColumnSortDirection property, and then do the sort by calling the Sort method.

The following example sorts a Listbox in descending order on the first column.

//first column, descending order
ListBox1.ColumnsortDirection(0)=ListBox.SortDescending
ListBox1.SortedColumn=0 //first column is the sort column
ListBox1.Sort

You can also sort a column based on the current value of ColumnSortDirection by calling the PressHeader method. This method programmatically clicks the header for the column passed to it.

## Custom Sorts

The following example uses the CompareRows event to sort columns of numbers. If you rely on default sorting, numbers are not sorted in numerical order, i.e., 2 is greater than 100 and less than 200.

Function CompareRows(row1 as Integer, row2 as Integer, column as Integer, ByRef result as Integer) as Boolean
If Val( Me.Cell(row1,column))> Val( Me.cell(row2,column)) then
result=1
else
result=-1
End if
Return True //tells RB to use the custom sort

With this code in place, the correct (numerical) sorting is done whenever the user clicks the header area. Test to be sure that the custom sort affects only the numerical columns.

To sort dates, store the TotalSeconds property of the Date in a column of width zero and sort the rows based on that column.

# See Also

DatabaseQuery, EditField controls; DatabaseField, ListColumn, RecordSet classes.