StringGridLib — Data Grid Control Library

Comprehensive functionality for creating and managing tabular data grids in Plan9Basic. StringGrid displays rows and columns of data with support for 9 column types (text, checkbox, currency, date, glyph, image, popup/dropdown, progress bar, time), in-cell editing, column sorting, clipboard operations, CSV import/export, popup column items, and a rich event system. 112 functions.

CategoryCountDescription
Error Handling4stringgrid_error, errormsg$, strerror$, clearerror
Creation & Destruction3stringgrid# (2 overloads), stringgrid_free
Row & Column Count3rowcount (get/set), colcount
Column Management4addcolumn#, deletecolumn, clearcolumns, column#
Column Properties11header, width, visible, readonly, type, align (get/set)
Cell Access8cell text (get/set), cellcheck (get/set), cellnum (get/set), cellprogress (get/set)
Row Operations9rowheight (get/set), insertrow, deleterow, moverow, swaprows, clearrow, copyrow, clearrows
Sorting2sort (text), sortnum (numeric)
Clipboard5copy, copysel, paste, copycell, pastecell
CSV Import/Export4exportcsv, importcsv, tocsv$, fromcsv
Selection5col (get/set), row (get/set), selectcell#
Grid Options10showhdr, editing, altcolors, colresize, rowselect (get/set)
Popup Columns3popupadd, popupclear, popupcount
Position & Size12x, y, width, height (get/set), bounds#, + align (get/set)
Visibility & State8visible, enabled, opacity (get/set) + focus, isfocused
Scrolling1scrolltorow
Tag & Parent4tag (get/set), parent# (get/set)
Events178 event types × set/get + clearcallbacks#

Cross-Platform Support

PlatformStatusNotes
Windows✅ Full SupportWin32/Win64
Linux✅ Full SupportGTK-based
Android✅ Full SupportMobile-optimized touch scrolling

Error Handling

FunctionSignatureDescription
stringgrid_error()stringgrid_error@Last error code (0 = no error)
stringgrid_errormsg$()stringgrid_errormsg$@Last error message as string
stringgrid_strerror$(code)stringgrid_strerror$@nDescription for a given error code
stringgrid_clearerror()stringgrid_clearerror@Clear the error state

Numeric Values Reference

Column Types stringgrid_addcolumn#

ValueConstantDescriptionCell Accessors
0COL_STRINGStandard text columncell$ / cell#
1COL_CHECKCheckbox columncellcheck / cellcheck#
2COL_CURRENCYCurrency formattedcell$ / cell#
3COL_DATEDate columncell$ / cell#
4COL_GLYPHGlyph/icon columncellnum / cellnum#
5COL_IMAGEImage columncell$ / cell#
6COL_POPUPDropdown/popup columncell$ / cell# + popup functions
7COL_PROGRESSProgress bar columncellprogress / cellprogress#
8COL_TIMETime columncell$ / cell#
ⓘ Note: Define column types as named constants for readability: let COL_STRING = 0, let COL_CHECK = 1, etc.

Control Alignment stringgrid_align#

ValueDescription
0None (absolute positioning)
1Top
2Left
3Right
4Bottom
9Client (fill parent)

Column Text Alignment stringgrid_columnalign#

ValueDescriptionUse Case
0CenterHeaders, status columns
1Leading (Left)Text, names, descriptions (default)
2Trailing (Right)Numbers, prices, quantities

Creation & Destruction

FunctionSignatureDescription
stringgrid#(parent#)stringgrid#@#Create grid with default size
stringgrid#(parent#, x, y, w, h)stringgrid#@#nnnnCreate with position and size
stringgrid_free(grid#)stringgrid_free@#Destroy grid and release resources
╯ plan9basic
let grid# = stringgrid#(frm#, 10, 10, 580, 350)

' Cleanup
stringgrid_free(grid#)

Row & Column Count

FunctionSignatureDescription
stringgrid_rowcount(grid#)stringgrid_rowcount@#Get the number of data rows
stringgrid_rowcount#(grid#, n)stringgrid_rowcount#@#nSet the number of data rows
stringgrid_colcount(grid#)stringgrid_colcount@#Get the number of columns
╯ plan9basic
' Add columns first, then set row count
stringgrid_addcolumn#(grid#, "Name", 0, 150)
stringgrid_addcolumn#(grid#, "Age", 0, 60)

' Create 10 empty rows
stringgrid_rowcount#(grid#, 10)

println "Rows: " + str$(stringgrid_rowcount(grid#))
println "Cols: " + str$(stringgrid_colcount(grid#))
⚠ Warning: Always add columns before setting the row count. Setting rows before columns may cause unexpected behavior.

Column Management

FunctionSignatureDescription
stringgrid_addcolumn#(grid#, header$, type, width)stringgrid_addcolumn#@#$nnAdd a column; returns column pointer
stringgrid_deletecolumn(grid#, col)stringgrid_deletecolumn@#nDelete column at index
stringgrid_clearcolumns(grid#)stringgrid_clearcolumns@#Remove all columns
stringgrid_column#(grid#, col)stringgrid_column#@#nGet column object pointer
╯ plan9basic
' Define column types
let COL_STRING = 0
let COL_CHECK = 1
let COL_PROGRESS = 7

' Add columns: header$, type, width
stringgrid_addcolumn#(grid#, "Done", COL_CHECK, 50)
stringgrid_addcolumn#(grid#, "Task", COL_STRING, 200)
stringgrid_addcolumn#(grid#, "Progress", COL_PROGRESS, 120)

' Delete last column
stringgrid_deletecolumn(grid#, 2)

' Clear and rebuild
stringgrid_clearcolumns(grid#)

Column Properties

FunctionSignatureDescription
stringgrid_columnheader$(grid#, col)stringgrid_columnheader$@#nGet column header text
stringgrid_columnheader#(grid#, col, text$)stringgrid_columnheader#@#n$Set column header text
stringgrid_columnwidth(grid#, col)stringgrid_columnwidth@#nGet column width in pixels
stringgrid_columnwidth#(grid#, col, w)stringgrid_columnwidth#@#nnSet column width
stringgrid_columnvisible(grid#, col)stringgrid_columnvisible@#nGet column visibility (0/1)
stringgrid_columnvisible#(grid#, col, n)stringgrid_columnvisible#@#nnShow/hide a column
stringgrid_columnreadonly(grid#, col)stringgrid_columnreadonly@#nGet column read-only state (0/1)
stringgrid_columnreadonly#(grid#, col, n)stringgrid_columnreadonly#@#nnSet column read-only
stringgrid_columntype(grid#, col)stringgrid_columntype@#nGet column type (0–8)
stringgrid_columnalign(grid#, col)stringgrid_columnalign@#nGet column text alignment (0=center, 1=left, 2=right)
stringgrid_columnalign#(grid#, col, align)stringgrid_columnalign#@#nnSet column text alignment
╯ plan9basic
' Right-align numeric columns
stringgrid_columnalign#(grid#, 1, 2)   ' Age
stringgrid_columnalign#(grid#, 2, 2)   ' Price

' Make ID column read-only
stringgrid_columnreadonly#(grid#, 0, 1)

' Hide a column without deleting it
stringgrid_columnvisible#(grid#, 3, 0)

' Rename a header
stringgrid_columnheader#(grid#, 0, "Full Name")

Cell Access

Different column types use different cell accessors. Text columns use cell$/cell#, checkbox columns use cellcheck, numeric columns use cellnum, and progress columns use cellprogress.

FunctionSignatureDescription
stringgrid_cell$(grid#, col, row)stringgrid_cell$@#nnGet cell text value
stringgrid_cell#(grid#, col, row, text$)stringgrid_cell#@#nn$Set cell text value
stringgrid_cellcheck(grid#, col, row)stringgrid_cellcheck@#nnGet checkbox state (0=unchecked, 1=checked)
stringgrid_cellcheck#(grid#, col, row, n)stringgrid_cellcheck#@#nnnSet checkbox state
stringgrid_cellnum(grid#, col, row)stringgrid_cellnum@#nnGet cell numeric value
stringgrid_cellnum#(grid#, col, row, value)stringgrid_cellnum#@#nnnSet cell numeric value
stringgrid_cellprogress(grid#, col, row)stringgrid_cellprogress@#nnGet progress value (0–100)
stringgrid_cellprogress#(grid#, col, row, value)stringgrid_cellprogress#@#nnnSet progress value (0–100)
╯ plan9basic
' Text cells (COL_STRING)
stringgrid_cell#(grid#, 0, 0, "Alice")
let name$ = stringgrid_cell$(grid#, 0, 0)

' Checkbox cells (COL_CHECK)
stringgrid_cellcheck#(grid#, 1, 0, 1)   ' Check it
let checked = stringgrid_cellcheck(grid#, 1, 0)

' Numeric cells (COL_GLYPH or generic)
stringgrid_cellnum#(grid#, 2, 0, 42)
let val = stringgrid_cellnum(grid#, 2, 0)

' Progress cells (COL_PROGRESS)
stringgrid_cellprogress#(grid#, 3, 0, 75)   ' 75%
let pct = stringgrid_cellprogress(grid#, 3, 0)
ⓘ Note: Cell coordinates are always (col, row) — column first, then row. Both are 0-based.

Row Operations

FunctionSignatureDescription
stringgrid_rowheight(grid#)stringgrid_rowheight@#Get row height in pixels
stringgrid_rowheight#(grid#, h)stringgrid_rowheight#@#nSet row height for all rows
stringgrid_insertrow(grid#, row)stringgrid_insertrow@#nInsert a new empty row at the given index
stringgrid_deleterow(grid#, row)stringgrid_deleterow@#nDelete the row at the given index
stringgrid_moverow(grid#, from, to)stringgrid_moverow@#nnMove a row from one index to another
stringgrid_swaprows(grid#, row1, row2)stringgrid_swaprows@#nnSwap two rows
stringgrid_clearrow(grid#, row)stringgrid_clearrow@#nClear all cell values in a row
stringgrid_copyrow(grid#, src, dest)stringgrid_copyrow@#nnCopy cell values from one row to another
stringgrid_clearrows(grid#)stringgrid_clearrows@#Clear all rows (remove all data)
╯ plan9basic
' Insert row at the top
stringgrid_insertrow(grid#, 0)

' Delete selected row
let r = stringgrid_row(grid#)
if r >= 0 then stringgrid_deleterow(grid#, r)

' Move row 3 to position 0
stringgrid_moverow(grid#, 3, 0)

' Swap rows 1 and 2
stringgrid_swaprows(grid#, 1, 2)

' Duplicate a row
stringgrid_rowcount#(grid#, stringgrid_rowcount(grid#) + 1)
stringgrid_copyrow(grid#, 0, stringgrid_rowcount(grid#) - 1)

Sorting

FunctionSignatureDescription
stringgrid_sort(grid#, col, asc)stringgrid_sort@#nnSort by column using text (alphabetical) comparison
stringgrid_sortnum(grid#, col, asc)stringgrid_sortnum@#nnSort by column using numeric comparison
╯ plan9basic
' Sort by name alphabetically (ascending)
stringgrid_sort(grid#, 0, 1)

' Sort by score numerically (descending)
stringgrid_sortnum(grid#, 2, 0)
⚠ Warning: Use stringgrid_sort for text columns (names, cities) and stringgrid_sortnum for numeric columns (ages, prices, scores). Using the wrong one produces incorrect ordering — e.g., text sort orders “9” after “10”.
ⓘ Note: The asc parameter: 1 = ascending (A→Z, 0→9), 0 = descending (Z→A, 9→0).

Clipboard

FunctionSignatureDescription
stringgrid_copy(grid#)stringgrid_copy@#Copy entire grid to clipboard (tab-delimited)
stringgrid_copysel(grid#)stringgrid_copysel@#Copy current selection to clipboard
stringgrid_paste(grid#)stringgrid_paste@#Paste from clipboard into the grid
stringgrid_copycell(grid#, col, row)stringgrid_copycell@#nnCopy a specific cell to clipboard
stringgrid_pastecell(grid#, col, row)stringgrid_pastecell@#nnPaste clipboard content into a specific cell
╯ plan9basic
' Copy entire grid
stringgrid_copy(grid#)

' Copy just the selected cell
let c = stringgrid_col(grid#)
let r = stringgrid_row(grid#)
stringgrid_copycell(grid#, c, r)

' Paste into specific cell
stringgrid_pastecell(grid#, 0, 0)

CSV Import / Export

FunctionSignatureDescription
stringgrid_exportcsv(grid#, file$, delim$, hdr)stringgrid_exportcsv@#$$nExport grid to a CSV file
stringgrid_importcsv(grid#, file$, delim$, hdr)stringgrid_importcsv@#$$nImport CSV file into the grid
stringgrid_tocsv$(grid#, delim$, hdr)stringgrid_tocsv$@#$nGet grid data as a CSV string
stringgrid_fromcsv(grid#, data$, delim$, hdr)stringgrid_fromcsv@#$$nLoad grid from a CSV string
╯ plan9basic
' Export to file with comma delimiter, including headers
stringgrid_exportcsv(grid#, "data.csv", ",", 1)

' Import from file
stringgrid_importcsv(grid#, "data.csv", ",", 1)

' Export to string (for println, HTTP, etc.)
let csv$ = stringgrid_tocsv$(grid#, ",", 1)
println csv$

' Load from string (e.g., from HTTP response)
stringgrid_fromcsv(grid#, csv$, ",", 1)

' Use semicolon delimiter
stringgrid_exportcsv(grid#, "data.csv", ";", 1)
ⓘ Note: The hdr parameter: 1 = include/expect column headers as the first row, 0 = data only. The delim$ parameter accepts any single character (“,”, “;”, tab, etc.).

Selection

FunctionSignatureDescription
stringgrid_col(grid#)stringgrid_col@#Get the currently selected column index
stringgrid_col#(grid#, col)stringgrid_col#@#nSet the selected column
stringgrid_row(grid#)stringgrid_row@#Get the currently selected row index
stringgrid_row#(grid#, row)stringgrid_row#@#nSet the selected row
stringgrid_selectcell#(grid#, col, row)stringgrid_selectcell#@#nnSelect a specific cell (sets both col and row)
╯ plan9basic
' Read current selection
let c = stringgrid_col(grid#)
let r = stringgrid_row(grid#)
println "Selected: col=" + str$(c) + " row=" + str$(r)

' Select a specific cell
stringgrid_selectcell#(grid#, 2, 5)

Grid Options

FunctionSignatureDescription
stringgrid_showhdr(grid#)stringgrid_showhdr@#Get show column headers (0/1)
stringgrid_showhdr#(grid#, n)stringgrid_showhdr#@#nShow/hide column headers
stringgrid_editing(grid#)stringgrid_editing@#Get editing enabled (0/1)
stringgrid_editing#(grid#, n)stringgrid_editing#@#nEnable/disable cell editing
stringgrid_altcolors(grid#)stringgrid_altcolors@#Get alternating row colors (0/1)
stringgrid_altcolors#(grid#, n)stringgrid_altcolors#@#nEnable/disable alternating row colors (zebra stripes)
stringgrid_colresize(grid#)stringgrid_colresize@#Get column resize enabled (0/1)
stringgrid_colresize#(grid#, n)stringgrid_colresize#@#nAllow/prevent user column resizing
stringgrid_rowselect(grid#)stringgrid_rowselect@#Get row selection mode (0/1)
stringgrid_rowselect#(grid#, n)stringgrid_rowselect#@#nEnable/disable full row selection mode
╯ plan9basic
' Typical grid setup
stringgrid_editing#(grid#, 1)       ' Allow cell editing
stringgrid_altcolors#(grid#, 1)    ' Zebra stripes
stringgrid_colresize#(grid#, 1)    ' User can resize columns
stringgrid_rowselect#(grid#, 0)    ' Cell selection (not full row)

' Read-only display grid
stringgrid_editing#(grid#, 0)
stringgrid_rowselect#(grid#, 1)    ' Full row highlighting

Position & Size

FunctionSignatureDescription
stringgrid_x(grid#)stringgrid_x@#Get X position
stringgrid_x#(grid#, x)stringgrid_x#@#nSet X position
stringgrid_y(grid#)stringgrid_y@#Get Y position
stringgrid_y#(grid#, y)stringgrid_y#@#nSet Y position
stringgrid_width(grid#)stringgrid_width@#Get width
stringgrid_width#(grid#, w)stringgrid_width#@#nSet width
stringgrid_height(grid#)stringgrid_height@#Get height
stringgrid_height#(grid#, h)stringgrid_height#@#nSet height
stringgrid_bounds#(grid#, x, y, w, h)stringgrid_bounds#@#nnnnSet position and size in one call
stringgrid_align(grid#)stringgrid_align@#Get control alignment
stringgrid_align#(grid#, n)stringgrid_align#@#nSet control alignment (0=none, 1=top, 2=left, 3=right, 4=bottom, 9=client)
╯ plan9basic
' Fill the client area of a form
stringgrid_align#(grid#, 9)

Visibility & State

FunctionSignatureDescription
stringgrid_visible(grid#)stringgrid_visible@#Get visibility (0/1)
stringgrid_visible#(grid#, n)stringgrid_visible#@#nSet visibility
stringgrid_enabled(grid#)stringgrid_enabled@#Get enabled state (0/1)
stringgrid_enabled#(grid#, n)stringgrid_enabled#@#nSet enabled state
stringgrid_opacity(grid#)stringgrid_opacity@#Get opacity (0.0–1.0)
stringgrid_opacity#(grid#, value)stringgrid_opacity#@#nSet opacity

Focus & Scrolling

FunctionSignatureDescription
stringgrid_focus(grid#)stringgrid_focus@#Set focus to the grid
stringgrid_isfocused(grid#)stringgrid_isfocused@#Is the grid focused? (0/1)
stringgrid_scrolltorow(grid#, row)stringgrid_scrolltorow@#nScroll the grid to make a specific row visible
╯ plan9basic
' Select and scroll to row 50
stringgrid_row#(grid#, 50)
stringgrid_scrolltorow(grid#, 50)

Tag & Parent

FunctionSignatureDescription
stringgrid_tag(grid#)stringgrid_tag@#Get user-defined integer tag
stringgrid_tag#(grid#, n)stringgrid_tag#@#nSet user-defined integer tag
stringgrid_parent#(grid#)stringgrid_parent#@#Get parent control pointer
stringgrid_parent#(grid#, parent#)stringgrid_parent#@##Move grid to a different parent

Events

Each event has a setter (stringgrid_onXXX#(grid#, func$)) and a getter (stringgrid_onXXX$(grid#)). Use stringgrid_clearcallbacks#(grid#) to disconnect all callbacks at once.

Cell & Selection Events

Event SetterGetterCallback SignatureWhen It Fires
stringgrid_oncellclick#(grid#, func$)stringgrid_oncellclick$(grid#)function name(sender#, col, row)A cell is clicked
stringgrid_oncelldblclick#(grid#, func$)stringgrid_oncelldblclick$(grid#)function name(sender#, col, row)A cell is double-clicked
stringgrid_onselectcell#(grid#, func$)stringgrid_onselectcell$(grid#)function name(sender#, col, row)A new cell is about to be selected
stringgrid_onselchanged#(grid#, func$)stringgrid_onselchanged$(grid#)function name(sender#)The selection has changed
stringgrid_oneditingdone#(grid#, func$)stringgrid_oneditingdone$(grid#)function name(sender#, col, row)User finishes editing a cell
ⓘ Note: OnEditingDone fires after the user presses Enter or clicks away from an edited cell. Use it to validate data or update dependent calculations.

Header & General Events

Event SetterGetterCallback SignatureWhen It Fires
stringgrid_onheaderclick#(grid#, func$)stringgrid_onheaderclick$(grid#)function name(sender#, col)A column header is clicked (ideal for sorting)
stringgrid_onclick#(grid#, func$)stringgrid_onclick$(grid#)function name(sender#)The grid control is clicked
stringgrid_onresize#(grid#, func$)stringgrid_onresize$(grid#)function name(sender#)The grid is resized
stringgrid_clearcallbacks#(grid#)Disconnect all event callbacks
ⓘ Note: OnHeaderClick is the standard pattern for implementing column sorting. The callback receives the column index so you can toggle ascending/descending sort.

Complete Examples

Data Entry Grid

╯ dataentry.bas
let COL_STRING = 0
let COL_CHECK = 1

function OnAddRow(sender#) local cnt
    cnt = stringgrid_rowcount(grid#)
    stringgrid_rowcount#(grid#, cnt + 1)
endfunction

function OnDelRow(sender#) local row
    row = stringgrid_row(grid#)
    if row >= 0 then stringgrid_deleterow(grid#, row)
endfunction

function OnEdit(sender#, col, row)
    println "Edited [" + str$(col) + "," + str$(row) + "]: " + stringgrid_cell$(sender#, col, row)
endfunction

let frm# = form#("Data Entry", 600, 400)
form_position#(frm#, 4)

let grid# = stringgrid#(frm#, 10, 10, 580, 300)
stringgrid_editing#(grid#, 1)
stringgrid_altcolors#(grid#, 1)

stringgrid_addcolumn#(grid#, "Product", COL_STRING, 150)
stringgrid_addcolumn#(grid#, "Quantity", COL_STRING, 80)
stringgrid_addcolumn#(grid#, "Price", COL_STRING, 80)
stringgrid_addcolumn#(grid#, "In Stock", COL_CHECK, 70)
stringgrid_columnalign#(grid#, 1, 2)
stringgrid_columnalign#(grid#, 2, 2)

stringgrid_rowcount#(grid#, 5)

let btnAdd# = button#(frm#, "Add Row", 10, 320, 100, 30)
let btnDel# = button#(frm#, "Delete Row", 120, 320, 100, 30)
button_onclick#(btnAdd#, "OnAddRow")
button_onclick#(btnDel#, "OnDelRow")
stringgrid_oneditingdone#(grid#, "OnEdit")

form_show(frm#)

while form_visible(frm#) = 1
    processmessages()
end while

Sortable Table

╯ sortable.bas
let sortCol = 0
let sortAsc = 1

function OnHeaderClick(sender#, col) local isNumeric
    if col = sortCol then
        sortAsc = 1 - sortAsc
    else
        sortCol = col
        sortAsc = 1
    endif

    isNumeric = 0
    if col = 1 then isNumeric = 1
    if col = 2 then isNumeric = 1

    if isNumeric = 1 then
        stringgrid_sortnum(sender#, col, sortAsc)
    else
        stringgrid_sort(sender#, col, sortAsc)
    endif
endfunction

let frm# = form#("Sortable Table", 500, 350)
form_position#(frm#, 4)

let grid# = stringgrid#(frm#, 10, 10, 480, 300)
stringgrid_addcolumn#(grid#, "Name", 0, 150)
stringgrid_addcolumn#(grid#, "Age", 0, 60)
stringgrid_addcolumn#(grid#, "Score", 0, 80)
stringgrid_columnalign#(grid#, 1, 2)
stringgrid_columnalign#(grid#, 2, 2)

stringgrid_rowcount#(grid#, 5)
stringgrid_cell#(grid#, 0, 0, "Alice") : stringgrid_cell#(grid#, 1, 0, "30") : stringgrid_cell#(grid#, 2, 0, "95")
stringgrid_cell#(grid#, 0, 1, "Bob") : stringgrid_cell#(grid#, 1, 1, "25") : stringgrid_cell#(grid#, 2, 1, "87")
stringgrid_cell#(grid#, 0, 2, "Carol") : stringgrid_cell#(grid#, 1, 2, "35") : stringgrid_cell#(grid#, 2, 2, "92")
stringgrid_cell#(grid#, 0, 3, "David") : stringgrid_cell#(grid#, 1, 3, "28") : stringgrid_cell#(grid#, 2, 3, "78")
stringgrid_cell#(grid#, 0, 4, "Eve") : stringgrid_cell#(grid#, 1, 4, "32") : stringgrid_cell#(grid#, 2, 4, "99")

stringgrid_onheaderclick#(grid#, "OnHeaderClick")

form_show(frm#)

while form_visible(frm#) = 1
    processmessages()
end while

CSV Import/Export

╯ csv.bas
function OnExport(sender#)
    stringgrid_exportcsv(grid#, "contacts.csv", ",", 1)
    println "Exported to contacts.csv"
endfunction

function OnImport(sender#)
    stringgrid_importcsv(grid#, "contacts.csv", ",", 1)
    println "Imported from contacts.csv"
endfunction

function OnToString(sender#) local csv$
    csv$ = stringgrid_tocsv$(grid#, ",", 1)
    println csv$
endfunction

let frm# = form#("CSV Demo", 600, 400)
form_position#(frm#, 4)

let grid# = stringgrid#(frm#, 10, 50, 580, 300)
stringgrid_editing#(grid#, 1)

stringgrid_addcolumn#(grid#, "Name", 0, 150)
stringgrid_addcolumn#(grid#, "Email", 0, 200)
stringgrid_addcolumn#(grid#, "Phone", 0, 150)

let btnExport# = button#(frm#, "Export CSV", 10, 10, 100, 30)
let btnImport# = button#(frm#, "Import CSV", 120, 10, 100, 30)
let btnToStr# = button#(frm#, "To String", 230, 10, 100, 30)
button_onclick#(btnExport#, "OnExport")
button_onclick#(btnImport#, "OnImport")
button_onclick#(btnToStr#, "OnToString")

form_show(frm#)

while form_visible(frm#) = 1
    processmessages()
end while

Task List (Checkbox + Progress)

╯ tasklist.bas
let COL_CHECK = 1
let COL_STRING = 0
let COL_PROGRESS = 7

function OnCellClick(sender#, col, row) local checked
    if col = 0 then
        checked = stringgrid_cellcheck(sender#, col, row)
        stringgrid_cellcheck#(sender#, col, row, 1 - checked)
        if checked = 0 then
            stringgrid_cellprogress#(sender#, 2, row, 100)
        endif
    endif
endfunction

let frm# = form#("Task List", 500, 350)
form_position#(frm#, 4)

let grid# = stringgrid#(frm#, 10, 10, 480, 300)
stringgrid_addcolumn#(grid#, "Done", COL_CHECK, 50)
stringgrid_addcolumn#(grid#, "Task", COL_STRING, 200)
stringgrid_addcolumn#(grid#, "Progress", COL_PROGRESS, 120)

stringgrid_rowcount#(grid#, 4)
stringgrid_cell#(grid#, 1, 0, "Design UI")
stringgrid_cellprogress#(grid#, 2, 0, 100)
stringgrid_cellcheck#(grid#, 0, 0, 1)

stringgrid_cell#(grid#, 1, 1, "Implement backend")
stringgrid_cellprogress#(grid#, 2, 1, 75)

stringgrid_cell#(grid#, 1, 2, "Write tests")
stringgrid_cellprogress#(grid#, 2, 2, 30)

stringgrid_cell#(grid#, 1, 3, "Deploy")
stringgrid_cellprogress#(grid#, 2, 3, 0)

stringgrid_oncellclick#(grid#, "OnCellClick")

form_show(frm#)

while form_visible(frm#) = 1
    processmessages()
end while

Best Practices

PracticeWhy
Define column type constantslet COL_STRING = 0 is far more readable than bare numbers
Add columns before rowsSet up the column structure, then call rowcount#
Right-align numeric columnsstringgrid_columnalign#(grid#, col, 2) for numbers, prices, scores
Use stringgrid_sortnum for numbersText sort orders “9” after “10”; numeric sort does not
Handle OnEditingDoneValidate data and update dependent cells after the user finishes editing
Use OnHeaderClick for sortingStandard UX pattern: click header to sort ascending, click again to toggle
Enable altcolors for readabilityZebra stripes help users track rows visually in large grids
Use scrolltorow after programmatic selectionEnsures the selected row is visible after setting row#
Use CSV functions for data exchangetocsv$ for in-memory processing, exportcsv for file output

Quick Reference

FunctionSignatureDescription
stringgrid_error / errormsg$ / strerror$ / clearerrorvariousError handling (4)
stringgrid#(parent#[, x, y, w, h])variousCreate (2 overloads)
stringgrid_free(grid#)stringgrid_free@#Destroy
stringgrid_rowcount / colcountvariousRow & column count (3)
stringgrid_addcolumn# / deletecolumn / clearcolumns / column#variousColumn management (4)
stringgrid_columnheader / width / visible / readonly / type / alignvariousColumn properties (11)
stringgrid_cell / cellcheck / cellnum / cellprogressvariousCell access (8)
stringgrid_rowheight / insertrow / deleterow / moverow / swaprows / clearrow / copyrow / clearrowsvariousRow operations (9)
stringgrid_sort / sortnumvariousSorting (2)
stringgrid_copy / copysel / paste / copycell / pastecellvariousClipboard (5)
stringgrid_exportcsv / importcsv / tocsv$ / fromcsvvariousCSV import/export (4)
stringgrid_col / row / selectcell#variousSelection (5)
stringgrid_showhdr / editing / altcolors / colresize / rowselectvariousGrid options (10)
stringgrid_popupadd / popupclear / popupcountvariousPopup columns (3)
stringgrid_x/y/width/height / bounds# / alignvariousPosition & size (12)
stringgrid_visible / enabled / opacity / focus / isfocused / scrolltorowvariousVisibility, focus & scrolling (9)
stringgrid_tag / parent#variousTag & parent (4)
stringgrid_oncellclick/oncelldblclick/onselectcell/onselchanged/oneditingdone/onheaderclick/onclick/onresizevariousEvents set+get (17)
stringgrid_clearcallbacks#stringgrid_clearcallbacks#@#Disconnect all events

112 functions. Part of the Plan9Basic GUI library system.