List Browsers part 2

From Vectorlab
Jump to: navigation, search
This vcor page needs language cleanup.

If you wish to help, clean up the page, then remove me deleting the line "{{Language cleanup}}". In the category:Language cleanup you'll find a list of all pages marked for cleanup.

List Browsers dissected Part 2: rows and cells. By Orso B. Schmid

Introduction Part 1: Creation and columns Part 2: Rows and cells

Rows and Cells

After painfully setting up Icon List, your Columns together with their List of Column Data Items, you can finally load rows of cells with content. If you followed this article attentively you might have noticed that there are many ways to achieve the same cell display. It is probably this large flexibility that makes List Browsers so difficult. But mind, there are differences between Mac and PC which restrict the choices.

A row can be inserted using InsertLBItem, which also inserts a string value in the first cell of the first column. Again to be noticed that List Browsers default on creating text lists. Each column inserted with default control type Static, left-aligned, and with Display Type Text. All is ready for a text list. If you don't wish to display particular content, your List Browser is set up and need only your string values.

If you have more than one column and/or need to set an icon in the first cell, there are further steps. This mainly depends on the column control type, if it uses a List of Column Data Items or not, or if you need to display special attributes like line styles, vector fills or color fills.

The column is the king, in List Browsers.

Settings Modifiers Cells set up
Insert rows using InsertLBItem.

Fill each cell in the row looping the available columns:

  • Column doesn't use Column Data Items :
  • should show only generic text or/and icon:
  • otherwise, change cell Owner:

Below you can see a snapshot of NNA's Organization dialog for Class details with some comments of how it could be possible to reproduce it:

LB NNAclasses.png

Create rows

Rows are created with InsertLBItem: each cell of the row just after creation will have Owner None. InsertLBItem also loads the first cell of the first column with a string. Other cells must be loaded separately with the routines listed in Load cells, according to their purpose. For specialized purposes like color fills or pen patterns, you need first to change cells' Owner Type.

InsertLBItem( dialogID:LONGINT;  componentID:LONGINT;  itemIndex:INTEGER;  itemString:STRING):INTEGER;

Inserts a row in a List Browser and returns the index of the newly inserted row, which is to be used for further cell settings. The first cell in the first column gets the string stored in "itemString". Use one of the routines listed in Load cells to set up other cells in the row.

the index of the newly inserted row, which is to be used for further cell settings.
the string for the first cell in the first column.

GetNumLBItems(dialogID: LONGINT; componentID:LONGINT): INTEGER;

Returns the count of rows in a list browser.

Column Owner Type

The Column Owner determines what to display in a cell. It should be set only for cells where special attributes need to be displayed, otherwise the default value should be sufficient ( None ). One might consider the name "Column Owner" misleading. I prefer to call it "Cell Owner", since it's a cell property.

The cell's Owner can be set with SetLBColumnOwnerDrawnType. It is important to change the Owner before loading the cells with values, otherwise cells will display unreliably. For example: cells set to display attributes such as color fills will show the plaguing string "AAAAA...." with any Display allowing Text. This doesn't happen if you set the cell's owner before the cell's value. See image below, showing cells whose Owner has been set after loading the cell values:

LB aaaaaString.png

It's not difficult to choose the optimal cell Owner:

the all-rounder the text flattener the Image-based set
(default upon creation). Whenever a static image or text is sufficient. It displays what is set in the column's Display Type: Text, or Image or both. Securely display text under imaged-based column controls, even if the Display Type is Image. Specialized image-based cells which barely leave doubts as to their function. Must have a column Display Type Image.

Below you find a short introduction to each Owner Type with illustrations of the cell's display. Mind that for the sake of experimenting, the illustrations show the display of every Owner under each of the seven available column's Control Types. This is not something that you'll ever do, nor should do: plan your column Control Types carefully according to the needed cells.

  • Patterns are not functional with Vectorscript.
  • Gradients and Images can theoretically display any named resource that is accessible with Index2Name.
  • Radio columns override any kind of cell Owner, so it's superfluous to set an Owner for cells belonging to these columns.
  • Owners dedicated to attributes always need a column with Display type Image.

SetLBColumnOwnerDrawnType( dialogID:LONGINT; componentID:LONGINT; itemIndex:INTEGER; subItemIndex:INTEGER; 

Sets the Owner for a cell. Again, this is only needed if the default value None is not proper. The owner must be changed before loading cells with values.

declare the owner using one of the constants listed below. It is advisable to store them in an external px file to be loaded as include whenever a dialog involves List Browsers.
  • kLBNotOwnerDrawn = 0; { None }
  • kLBSolidRect = 1;
  • kLBDualSolidRect = 2;
  • kLBPatRect = 3;
  • kLBDualPatRect = 4;
  • kLBGradientOrImage = 5; { This you could call "Indexed Resources" }
  • kLBBlankOwnerDrawn = 6; { blank }
  • kLBTxtOwnerDrawn = 7; { text }
  • kLBDashedLine = 8;
a boolean value, but I couldn't get this to return FALSE.

GetLBColumnOwnerDrawnType( dialogID:LONGINT; componentID:LONGINT; itemIndex:INTEGER; subItemIndex:INTEGER; 

It seems to be impossible to get the owner of a cell. The value returned is always "0". Must be a bug.

Load a gradient cell
{ resource icon at column 1 }
IF SetLBColumnOwnerDrawnType(dlog, LB, row, col, kLBGradientOrImage) THEN { vectorfll owner }
	temp_b := SetLBItemGradientOrImageRefNumber(dlog, LB, row, col, Name2Index(resName));


LB ownerNoneImg.png

None is a flexible generic cell's Owner optimal to display text, numbers and icons, without any specialization. Is the default owner upon creating a row of cells using InsertLBItem. This owner is always the right choice whenever you don't need to reproduce attributes cells like line styles, colors or thumbnails. This is also the best choice whenever you plan to use a list of Column Data Items (since this consists in strings and icons). In the image above you see cells whose columns are set to Display Image but no Column Data Items are loaded, thus columns with control types dedicated to Data Items don't have any particular behavior.

For this to work you need


LB ownerBlankImg.png

Blank is an image-only cell's Owner. At first it bewilders: it displays absolutely nothing. Blank won't ever load an image and is meaningless for Display Type Text. In the image above the Display is Image.

Due to the (declared) lack of display, you can use it to blank out image cells without upsetting image data. It positively shows an empty cell, if Item or Edit Display is Image.

"Blank" doesn't affect images loaded from a List of Column Data Items, though, all control types behave as expected also for Blank cells, whereby you won't bother to set such a cell owner and then use Data Items. But is possible.

Cells whose column have control type Radio are not affected in the least and don't blank.

For this to work you need


LB ownerTxtImg.png

Text owned cells can only display text, icons eventually loaded into the cell will be ignored. This can be used to coerce text. In the image above we loaded the cells with numbers.

There is a peculiarity. If a Column Data Items with both text and images is available to the cell's column, you can observe:

This is kind of unexpected. You'd expect the image to drop here as well.

For this to work you need
If you set the Item/Edit Display to Image you'll correct the unexpected behavior of the cells using Column Data Items. If you don't have a list of Column Data Items set up, the Display is irrelevant. I know that it sounds mind-boggling to be able to to coerce a text display imposing a Display Image, but so is it.

Solid Rect

LB ownerSolidRectImg.png

Solid Rect is an image-based Owner for drawing color-filled cells. It's well used together will the owner Blank, when you need to show the absence of color (see the Organization dialog, Classes' details).

For this to work you need

Dual Solid Rect

Dual Solid Rect seems to be an owner that cannot be displayed. I believe that it should show a cell similar to Solid Rect but with a thick border. Setting a cell to this owner turns it into None (I believe. But since GetLBColumnOwnerDrawnType is not operational, I can't be really sure). I don't recall ever seeing a NNA list browser cell displaying a Dual Solid Rect, or a Dual Pattern, by the way.

Gradients and Images

LB ownerGradImg.png

Gradients and Images could do more than displaying only Gradients, it can actually display any kind of resource whose name is parsable with Index2Name. Unfortunately there are screen issues (distortion of the resource thumbnails). The tiny thumbnails generated indeed make this owner only apt for displaying Gradients, I find it too little also for Image resources. In the Organization dialog hatches are notably set to display only the name (a radical Text owner, I suppose), which is the better choice. Gradients cells display indeed well. See Example: rows for an experimental example about this owner.

Be careful in loading symbols or textures. They can kill your dialog for excessive CPU usage and under circumstances run you out of memory. There is a memory leak connected with resource thumbnails which affects the whole session.

For this to work you need

Dashed Line

LB ownerDashImg.png

Dashed Line cells can represent the attributes of a styled line (style and thickness) over a colored background. In the image above the background color has been given a light gray (darker on PC) for better understanding. Most of the times the background needs to be set to white.

For this to work you need

Pattern and Dual Pattern

LB ownerPatImg.png

With VW 2010 the functions GetLBItemPatternIndex and SetLBItemPatternIndex have been introduced allowing to display also patterns. To my knowledge there is no way to control pattern cells in List Browsers before VW 2010. On Mac the cells display random color and pattern values (see image above), on PC they are at least stable in the display.

On PC there is sometimes a heavy CPU issue connected with setting all four Color routines in cells whose Owner is pattern: the CPU will run at maximum and never stop until document closed. Simply avoid unwanted Pattern Owners in your cell. Since Patterns are officially not supported, this cannot be considered a bug.

Load cells

After setting up the cell's Owner Type (optional, whenever Owner None is not sufficient) you can load cells with values. If your cell is generic you can use SetLBItemInfo to load it. This call is proper for cells whose column has Control type:

temp_b := SetLBItemInfo( dialogID:LONGINT; componentID:LONGINT; itemIndex:INTEGER; subItemIndex:INTEGER; 
	itemString:STRING; imageIndex:INTEGER):BOOLEAN;
GetLBItemInfo(dialogID: LONGINT; componentID: LONGINT; itemIndex: INTEGER; subItemIndex: INTEGER; 
	VAR itemString: STRING; VAR imageIndex: INTEGER): BOOLEAN;

Gets/sets string and icon in a cell identified by row index "itemIndex" and column index "subItemIndex".

the string value for a cell. Can be empty.
the icon to be displayed in the cell. The icon index is related to the sequence generated while creating the List Browser's Icon List. If you need icons, the list must be created first. Pass "-1" if you don't need any icon.

SetLBItemData(nDialogID, nComponentID :LONGINT; nItemIndex, nSubItemIndex :INTEGER; 
	nUserData :LONGINT);

No idea. Why the parameter "nUserData"

  1. is called "user data"?
  2. expects a longint?

Please see User Data.

FindLBColumnItem( dialogID:LONGINT; componentID:LONGINT; columnIndex:INTEGER; 
	itemString:STRING; VAR itemIndex:INTEGER):BOOLEAN;

Searches a column for a certain string "itemString". Returns the found row index "itemIndex" or "-1".

Column Data Items cells

After setting up the List of Column Data Items for a column, cells need to be initialized to a proper list value. This can be done with SetLBItemUsingColumnDataItem. This is the call to be used for cells whose columns have control type:

SetLBItemUsingColumnDataItem( dialogID:LONGINT; componentID:LONGINT; itemIndex:INTEGER; subItemIndex:INTEGER; 

Assigns a Column Data Item by index (0-based) to a cell, all data types (string, image) defined in the chosen "columnDataItemIndex" will apply. Choose the appropriate Edit Display type (text, strings or both). You should use this call to initialize your cells whenever their column uses a List of Data Items. Avoid using SetLBItemInfo.

false if no Column Data Items are available for the cell's column (because you didn't set them up with InsertLBColumnDataItem).

Multiple Icons cells

For cells whose columns have control type:

GetLBMultImageIndexes( dialogID:LONGINT; componentID:LONGINT; itemIndex:INTEGER; subItemIndex:INTEGER; 
	VAR imageIndex0:INTEGER; VAR imageIndex1:INTEGER; VAR imageIndex2:INTEGER):BOOLEAN;
SetLBMultImageIndexes( dialogID:LONGINT; componentID:LONGINT; itemIndex:INTEGER; subItemIndex:INTEGER; 
	imageIndex0:INTEGER; imageIndex1:INTEGER; imageIndex2:INTEGER):BOOLEAN;

Gets/sets indexes of three icons from a resource file. This is only used by cells whose column Control is Multiple Icons and seems to work only on Mac. The indexes are constructed like in AddLBImage.

Color Cells

Special image-based cells dedicated to represent solid fills. Sets colors for cells whose Owner is:

Pattern cells use them too, but you can't use them on VW before 2010.

GetLBItemFillForeColor( dialogID:LONGINT; componentID:LONGINT; itemIndex:INTEGER; subItemIndex:INTEGER; 
SetLBItemFillForeColor( dialogID:LONGINT; componentID:LONGINT; itemIndex:INTEGER; subItemIndex:INTEGER; 
	redIndex:INTEGER; greenIndex:INTEGER; blueIndex:INTEGER):BOOLEAN;

GetLBItemFillBackColor( dialogID: LONGINT; componentID: LONGINT; itemIndex: INTEGER; subItemIndex: INTEGER; 
	VAR redIndex: INTEGER; VAR greenIndex: INTEGER; VAR blueIndex: INTEGER): BOOLEAN;
SetLBItemFillBackColor( dialogID:LONGINT; componentID:LONGINT; itemIndex:INTEGER; subItemIndex:INTEGER; 
	redIndex:INTEGER; greenIndex:INTEGER; blueIndex:INTEGER):BOOLEAN;

GetLBItemPenForeColor( dialogID:LONGINT; componentID:LONGINT; itemIndex:INTEGER; subItemIndex:INTEGER; 
SetLBItemPenForeColor(dialogID, componentID :LONGINT; itemIndex, subItemIndex, 
	redIndex, greenIndex, blueIndex :INTEGER) :BOOLEAN;

GetLBItemPenBackColor( dialogID:LONGINT; componentID:LONGINT; itemIndex:INTEGER; subItemIndex:INTEGER; 
SetLBItemPenBackColor( dialogID:LONGINT; componentID:LONGINT; itemIndex:INTEGER; subItemIndex:INTEGER; 
	redIndex:INTEGER; greenIndex:INTEGER; blueIndex:INTEGER):BOOLEAN;

Get or set a Solid Rect or Dashed Line cell's colors (see relative chaptes for where these calls might apply).

Indexed Resources

Special image-based cells dedicated to represent vectorfills. For cells whose Owner is:

SetLBItemGradientOrImageRefNumber( dialogID:LONGINT; componentID:LONGINT; itemIndex:INTEGER; subItemIndex:INTEGER; 
expects a positive index value. Since usually Vectorscript routines return vectorflls indexes negative, reverse the value: <code="vss">vecFill := -FFillPat;</code> or <code="vss">vecFill := -GetFPat(h);</code>. You treat it like you would use it in Index2name.
GetLBItemGradientOrImageRefNumber( dialogID:LONGINT; componentID:LONGINT; itemIndex:INTEGER; subItemIndex:INTEGER; 

Queries a Gradients and Images cell for the displayed indexed resource.

Line Styles

Special image-based cells dedicated to represent line styles. For cells whose Owner is:

SetLBItemDashStyle( dialogID:LONGINT; componentID:LONGINT; itemIndex:INTEGER; subItemIndex:INTEGER; 
	styleIndex:INTEGER; lineWeight:INTEGER):BOOLEAN;
value needs to be positive. Since commonly Vectorscript routines return the index negative, reverse the value: "penPatVal := - FPenPat;" or "penPatVal := - GetLS(h);"
the line weight as commonly returned by for example "FPenSize".
GetLBItemDashStyle(dialogID, componentID :LONGINT; itemIndex :INTEGER; subItemIndex :INTEGER; 
	VAR styleIndex, VAR lineWeight :INTEGER) :BOOLEAN;

Queries a Dashed Line cell for its displayed line attributes.

Text attributes

Sets text attributes for cells whose columns have control type:

Text cells default on left-alignment, black, plain. But this can be modified:

SetLBItemTextColor( dialogID:LONGINT; componentID:LONGINT; itemIndex:INTEGER; subItemIndex:INTEGER; 
	redIndex:INTEGER; greenIndex:INTEGER; blueIndex:INTEGER):BOOLEAN;

Default: black. Pass an r, g, b triplet in range 0-255 to modify the text color.

SetLBItemTextJust( dialogID:LONGINT; componentID:LONGINT; itemIndex:INTEGER; subItemIndex:INTEGER; 

Default: left. Pass one of the constants below to modify the justification:

  • k_TxtLeftJust = 1; { left }
  • k_TxtCenJust = 2; { center }
  • k_TxtRighJust = 3; { right }

SetLBItemTextStyle( dialogID:LONGINT; componentID:LONGINT; itemIndex:INTEGER; subItemIndex:INTEGER; 

Default: Plain. Pass one of the constants below to modify the style:

  • k_TxtStyPlain = 0;
  • k_TxtStyBold = 1;
  • k_TxtStyItalic = 2;
  • k_TxtStyUnderl = 4;

Example: rows

Create a resource list by object type with tiny, incomprehensible thumbnails
{ ************************************************ }

loads cells with resource thumbnails and names.
The routine below doesn't check for valid resource types.
This will work with all resources creating a thumbnail in the Resource Browser, and
any resource parsable with Name2Index.
This can positively run you out of memory on heavy resource thumbnails like textures.

Use the object type constants for setting the resource to be loaded, for example:

16: sym definition
66: hatch definition
97: texture definition EXTREEEEMELY heavy, any user click causes the watch-cursor
119: image fill definition
120: gradient fill definition
127: wall style

Thus you can see how bad thumbnails aside of gradients display.

The list browser receiving this routine should have the first two columns 
both with control type Static and Item Display Image, 
col 0: we load the resource name
col 1: we load the resource thumbnail

PROCEDURE LB_LoadIndexedResources(dlog, LB: INTEGER; resType: INTEGER);
		resList, cnt : LONGINT;
		resName : STRING;
		row, col : INTEGER;
		temp_b : BOOLEAN;
		IF DeleteAllLBItems(dlog, LB) THEN BEGIN
			EnableLBUpdates(dlog, LB, FALSE);
			resList := BuildResourceList(resType, 0, '', cnt); 
			{ if you did some resource renaming during dialog execution this can mess up the list, 
			in this case you better redo the list.
			You don't need to redo it if your resList is not modified during dialog execution }
			IF cnt > 0 THEN BEGIN
				row := -1;
				col := 0;
				WHILE (row+1) < cnt DO BEGIN
					resName := GetNameFromResourceList(resList, row+2); { this is a 1-based list }
					{ resource name at column 0 }
					row := InsertLBItem(dlog, LB, GetNumLBItems(dlog, LB), resName);
					temp_b := SetLBColumnOwnerDrawnType(dlog, LB, row, col, kLBTxtOwnerDrawn); { text owner }
					{ resource icon at column 1 }
					IF SetLBColumnOwnerDrawnType(dlog, LB, row, col +1, kLBGradientOrImage) THEN { vectorfill owner }
						temp_b := SetLBItemGradientOrImageRefNumber(dlog, LB, row, col+1, Name2Index(resName));
				AlrtDialog(Concat('Resource list for object type "' , resType, '" is either empty or not parsable') );
			EnableLBUpdates(dlog, LB, TRUE);
			temp_b := RefreshLB(dlog, LB);
	END; { LB_LoadIndexedResources }

List Browser usage

Your List Browser is now set up, you have columns and data in each cell. There comes the need to manage the List Browser data upon user's actions, or trigger special behaviors. List Browsers allow for quite nice features in this sense:

List Browsers Events

A List Browser is capable of returning an event on user's click. Using events offers an easy way to store the last selected row and column. It is always advisable to use events since this reduces your code. The user's selection is parsable through GetLBEventInfo. It returns a set of three informations:

  • event triggered
  • row selected
  • column selected

There is a subtle difference in the type of event triggered, depending on the control type of the column involved. Documented is the event "-4", upon row selection, but undocumented is that the event returned can also be "-2" if the cell clicked belongs to a column whose Control Type uses Column Data Items.

Mind please that the row returned might be "-1" if the user clicks on white space within the List Browser. This can happen if your List Browser is higher than the rows displayed. After the last row some white space displays and clicks on this space cause event "-1".

It is *highly* recommendable to parse for List Browsers events exclusively in the dialog events CASE statement, straight where you code the response to user's action on the listBrowserID item. The event returned will be "-1" if the List Browsers hasn't been clicked at least once (for example just at dialog start). Or even more misleading values. Read this attentively. [EDIT Dieter Geerts 2014-03-15] : I could not reproduce the '-1' event type. I guess it is solved in the current version of VW, 2014.

GetLBEventInfo( dialogID:LONGINT; componentID:LONGINT; 

Sets variables with the last event raised, the last selected row and last selected column.

  • You should always use this routine in an IF condition for the case that it returns FALSE.
  • You shall pay a lot of care if you call GetLBEventInfo outside the CASE statement for the involved List Browser item ID. See below. GetLBEventInfo should be used exclusively in the CASE statement for your List Browser. The events returned are not reliable otherwise.
  • If you store selection indexes in global variables, you'll do well to initialize them to -1 and code accordingly. If for some reason GetLBEventInfo returns false, you risk misleading values otherwise (for example "0" which would lead to believe a first column or row has selected).
  • Setting a sorting column somewhere in your script raises an event. I believe it shouldn't. This will record as last user event and will mislead you. Be careful if you change sorting column after column creation.
Event flags
  • no event: row selection for on-click-toggle control types but no Data Items List is available
  • event = -2: row selection for on-click-toggle control types when items are available
  • event = -4: row selection for control types Static and Number
  • event = -8: the user scrolls the LB using the arrow keys. This event sets no vars for the rows or cols: rowIndex is always -1, columIndex is always -1. The involved row will highlight nevertheless. But you have no way to find out what is selected until the user actually clicks on a row.
  • event = -9: the user types a string while the focus is on the LB. This behaves the same as event -8. From Julian Carr on the V-list.
  • event = -10: column selection
Events on drag-drop

[DG - 2014-03-16]

You need to use the data that comes with the dialog event handler here to catch the drag-drop events!

  • event = -4; data = rowIndex: When starting, a normal event is thrown, so just a select event.
  • event = -4; data = -50: The first event on releasing the item(s).
  • event = -4; data = 43703408: The second event on releasing the item(s).
  • event = -4; data = -51: The third and last event on releasing the item(s).

So check for the -51 data to react on drag-drop of items.

And here I go. Orso's usual dissection:

Beastly misleading
  • event = 0: you called GetLBEventInfo outside the CASE block AND didn't set a sorting column somewhere in your code
  • eventRow = 0: see above
  • eventCol = 0: see above
  • event = -10: you called GetLBEventInfo outside the CASE block AND set a sorting column somewhere in your code!
  • eventRow = -1: see above
  • eventCol = 0: see above
Probable hint of crashy VW
  • event = -<long integer value>: you called GetLBEventInfo outside the CASE block and you got some garbage value from previous codes. I saw this happen but cannot reproduce.
  • eventRow = -<long integer value>: see above
  • eventCol = -<long integer value>: see above
  • eventRow = -1: clicking on white space below the last row (for example when your row count doesn't fill the whole available List Browser space).
  • eventCol = -1: see above.
  • eventCol = 0: default value, before first user's click on the List Browser (for example just after dialog launch). this might be regarded as a bug.
  • Resizing columns doesn't return an event parsable with GetLBEventInfo.
it can be observed that image based control types behave differently:
  • event = -2: row selection for control types: Static Icon, Multiple Icons
  • event = -2: row selection for control types: Radio, Multi State, Single Instance when Column Data Items are defined.
Must have something to do with the Column Data Items wanting to change or these controls expecting them.
Returns FALSE
  • on row selection for control types: Radio, Multi State, Single Instance when Column Data Items are missing (this is a hint that you forgot to set up Column Data Items).
Unexpectedly returns TRUE
  • calling the routine before first user's click on the List Browser (for example just after dialog launch).... and you called it outside the CASE statement for your List Browser ID.

{put in SetupDialogC case }
CASE item OF
		{ ... }
listBrowserID: IF GetLBEventInfo(dlog, listBrowserID, event, eventRow, eventCol) THEN
		alrtDialog(concat('event: ', event, ' last sel row: ', eventRow, ' last sel col: ', eventCol));

{ ... }

Drag and drop

Drag and drop permits to the user to change order of the rows. This feature is disabled by default. Upon selecting a row and beginning a drag, the List Browser will wait for the user to drop it at some other position: it triggers user interaction. This feature can only be set on columns with control Number. Other columns will simply behave weird. An example can be observed on the "Design Layers" tab of NNA's Organizations dialog, when the option "Details" is activated: the stacking order is dealt with Drag and Drop.

This feature can be made even more interesting when combined with List Browsers Events. At this point you can pass rows of data between two List Browsers. If you code such functionality you must pay great attention to reset the last selected indexes of both List Browsers. There is to my knowledge no NNA dialog using drag and drop between two (or more) List Browsers so user's of your dialog won't recognize the possibility on their own. They are not used to it. Abundant alerts should inform the user that a drop on another List Browser is awaited. Then it works like a charm. Among my favorites, but it takes some heavy coding to set up properly.

For this to work you need
on Windows during the user interaction (when the user is dragging a row) the whole screen in all monitors will flicker mercilessly, excluding the Vectorworks document window, which stay put and quiet. This is a long standing issue, but after all we don't look at anything else but our List Browser, while we are about to drop rows. (Fixed by VW 14 SP3)

EnableLBDragAndDrop(dlog: LONGINT; LB: LONGINT; 
	enable: BOOLEAN) : BOOLEAN;

Enables/disables drag and drop on a List Browser. It only works for columns of type Number.

always TRUE, like SetLBDragDropColumn. Simply pass the result to a temporary boolean variable that you can trash.

SetLBDragDropColumn( dlog : LONGINT; LB : LONGINT; 
	columnIndex: INTEGER) : BOOLEAN;

Declare the drag and drop column.

it doesn't look capable of returning false. Even if the List Browser doesn't have a single column, it will happily return TRUE.

See also: Using List Browsers Events

EnableLBDropOnIndices(dlog, LB : LONGINT; 
	iStartIndex, iEndIndex : INTEGER; bEnable : BOOLEAN) : BOOLEAN;

By default all rows accept dropping. With this call is possible to disable, or re-enable, some rows from dropping. It shall be noticed that this is targeting rows, not columns.

iStartIndex, iEndIndex
range of rows where dropping is disabled.


By default sorting is activated upon creating a List Browser, but it's possible to enable, disable and set sorting order (ascending or descending) during dialog execution. A few observations:

  • Sorting image cells is meaningless, you can disable it since it will only upset your user.
  • Sorting text cells on Mac behaves differently than on PC: this I wish to explore further.
  • Sorting numeric cells sort them alphabetically, see the cell Owner Number.
setting a sorting column by script raises a List Browser event, which is a bug. This should only happen on user interaction. See List Browsers Events.

EnableLBSorting(dialogID: LONGINT; componentI LONGINT; 
	enableSorting: BOOLEAN);

Enables/disables sorting for a whole List Browser. Sorting is TRUE by default.

GetLBColumnSortState( dialogID:LONGINT; componentID:LONGINT; 

Returns sorting state of a column.

GetLBSortColumn( dialogID:LONGINT; componentID:LONGINT):INTEGER;

SetLBSortColumn( dialogID:LONGINT; componentID:LONGINT; columnIndex:INTEGER; 

Returns or set the current sort column in a List Browser whose sorting is enabled.


Script-side selection is very well combined with List Browsers Events, which allows to keep easy track of the last user's selection, if any.

SetLBSelection(dialogID: LONGINT; componentID: LONGINT; 
	firstItemIndex: INTEGER; lastItemIndex: INTEGER; select: BOOLEAN): BOOLEAN;

Select/deselects a range of rows from row "firstItemIndex" to row "lastItemIndex". If the selection is outside the List Browser's scroll window, the window will automatically scroll to show the selection. See EnsureLBItemIsVisible for a similar behavior, but without selection.

GetNumSelectedLBItems(dialogID: LONGINT; componentID: LONGINT): INTEGER;

Count of currently selected rows. Among the most used routines, allows to loop down all selected rows for processing. Using List Browsers Events the last selected row and column will be collected. But this still doesn't inform you on other rows eventually selected. This check is not needed if EnableLBSingleLineSelection is active: the user cannot select more than one line.

IsLBItemSelected(dialogID: LONGINT; componentID: LONGINT; 
	itemIndex: INTEGER): BOOLEAN;

Checks if row "itemIndex" is currently selected (itemIndex = rowIndex). Like the previous routine, this check is not needed if EnableLBSingleLineSelection' is active and the script parses for List Browsers Events.

EnableLBSingleLineSelection(dialogID: LONGINT; componentID: LONGINT; 

Enable/disable selection of multiple rows. By default multiple selection is enabled. This is affects the whole list browser.

EnableLBClickAllDataChange(dialogID: LONGINT; componentID: LONGINT; 

Enable/disable changing all cells' values in a column when a modifier key (ctrl/alt on Mac/PC) is pressed. An example of this feature is the Navigation palette: the visibility column. Upon clicking on a row and while multiple rows are selected, the value displayed in the clicked row will apply to all selected rows. Is a good feature that can save some code, if your script allows for this functionality.

		temp_b : BOOLEAN;
		IF GetNumLBItems(d, LB) > 0 THEN
			temp_b := SetLBSelection(d, LB, 0, GetNumLBItems(d, LB)-1, select);
		i : INTEGER;
		temp_b : BOOLEAN;
		i := 0;
		WHILE i < GetNumLBItems(d, LB) DO
			IF IsLBItemSelected(d, LB, i) THEN
				temp_b := DeleteLBItem(d, LB, i)
				i := i + 1;

Destroy data

Sometimes what you need is to destroy data. Below the routine allowing for this to be achieved:

DeleteLBColumn(dialogID: LONGINT; componentID: LONGINT; 
	columnIndex: INTEGER): BOOLEAN;

Remove the column at "columnIndex" from the List Browser. Is seldom, but it can happen that you need to rebuild the List Browser's columns anew. One such circumstance is that you need to re-write the columns titles.

BUG: If you pass the index of a column (NOT Column Data Item) for deleting it, and this doesn't exists, the application will crash (VW 13 and 14).

RemoveAllLBColumnDataItems( dialogID:LONGINT; componentID:LONGINT; 

Remove all items in the List of Column Data Items defined for the column at "columnIndex".

RemoveLBColumnDataItem( dialogID:LONGINT; componentID:LONGINT; 
	columnIndex:INTEGER; columnDataItemIndex:INTEGER):BOOLEAN;

Remove the chosen item at "columnDataItemIndex" from the List of Column Data Items defined for the column at "columnIndex".

DeleteAllLBItems(dialogID: LONGINT; componentID: LONGINT): BOOLEAN;

Remove all rows in List Browser.

DeleteLBItem(dialogID: LONGINT; componentID: LONGINT; 
	itemIndex: INTEGER): BOOLEAN;

Remove the row at "itemIndex".

Update and Refresh

When the entire content of your List Browser shall be rebuilt destroying or modifying rows in a loop, you should always remember to disable the List Browser updates using EnableLBUpdates before the loop and reactivate it after the loop has finished executing. If you don't, you might bog down VW, which will recalculate the List Browser at each row, each cell inserted. Don't forget to re-enable updates otherwise you get a white list browser according to the event sent on user's click.

To be noted that enabling doesn't really work well on PC (!expand!).

A List Browser refresh might be needed after changing row's data. This applies specially for resource images which might be changing during your script execution. The routine RefreshLB coerces the refresh. If you change data in a loop please remember to keep "RefreshLB" out of it. This call should always be the last thing you do after renewing data in any fashion. This is among the most important settings when you are dealing with heavy data.

EnableLBUpdates(liDialogID, liComponentID :LONGINT; 
	bEnableUpdates :BOOLEAN);

Enable/disable updating cells in a List Browser. Disable always before looping rows with content modification and re-enable after the loop is terminated.

RefreshLB( dialogID:LONGINT; componentID:LONGINT):BOOLEAN;

Redraws the whole List Browser. Keep this outside content modifications involving loops.


Useful routines for ensuring that List Browsers are visible:

SetFocusOnLB( dialogID:LONGINT; componentID:LONGINT):BOOLEAN;

EnsureLBItemIsVisible( dialogID:LONGINT; componentID:LONGINT; 

Make the List Browser window display a row even if outside the scroll bounds of the list browser's window. Upon refreshing a List Browser the displayed rows always start at row "0". You don't need this call if you coerce a selection with SetLBSelection: the selection will scroll automatically the window to fit.


Sometimes you need to make your List Browser not accessible to the user, this can be obtained with EnableLB. The List Browser displays grayed and any user interaction is disabled ( Drag and Drop, on-click events). The List Browser is nevertheless fully enabled internally: you can still change content and display by script and it will update as expected.

See Drag and Drop for disabling dropping only on a range of rows (instead of disabling the whole list browser).

EnableLB(dialogID: LONGINT; componentID: LONGINT; 

Enable/disable a List Browser. Disabled: not clickable and grayed.

Orso's To do list

Here are things that I didn't understand yet. As soon as I understand that I didn't understand something, I list it here. The drama is that most of the times one is certain to have understood. And is not aware of what is missing.

  • sorting differences between Mac and PC
  • user data

User Data

Elsewhere called "itemData". Never figured this out.

One could think that it expects a resource index, similar to CreateControl, but it doesn't. Since it demands a Longint, I also tipped on indexed resources (records, symbols, images...) or a list created with BuildResourceList, but this must be a wrong concept. Another possibility is that it expects a handle to some sub-object, but we cannot pass it with Vectorscript. For example user data objects of type 76 accompanying some parametric objects or records of certain kinds (textures, sketches...). Last possibility: too much fantasy and is just unsupported.

Or does it perhaps await data from another dialog?

I have no idea if this should be initialized to "0" or "-1". And I still didn't encounter a situation where this matters. Intuitively I'd say that init on "0" is well.

You will find a reference to this mysterious type of data in the routines below:

targets a column and add something to the particular column data item using the parameter "itemData"? What is that we can add here?
securely get infos of the mysterious thing added at the particular column data item. Whatever that might be.
broken in VW 2017, either this or GetLBItemData doesn't work any longer, but it did. It targets a cell. Changing it to any possible value didn't ever produce a result by me, but the value set with it could be fetched using GetLBItemData.
broken in VW 2017, either this or SetLBItemData doesn't work any longer, but it did, returning what was set with SetLBItemData.
Introduction Part 1: Creation and columns Part 2: Rows and cells