Since there haven't been many class/object demos, I thought I would submit one that might be useful. It is simply a listbox with some headers at the top which can be clicked for sorting, similar to listview controls. The only reason to create it in a class is to keep the data encapsulated. Everything above FUNCTION PBMAIN() could be kept in an include file if desired.

Code:
#DIM ALL
#INCLUDE "win32api.inc"
'#INCLUDE "debug.inc"
%ID_List   = 100 'headers use 101, 102, ...
%ID_Status = 201
GLOBAL colList AS IColumnList

CLASS CColumnList
   INSTANCE hDlg, listID AS LONG       'handle of listbox parent, ID of listbox
   INSTANCE x, y, xx, yy AS LONG       'location & size of listbox
   INSTANCE numColumns AS LONG         'number of columns in listbox
   INSTANCE numItems AS LONG           'number of items in listbox
   INSTANCE hiLite AS LONG             'high lighted item in listbox 
   INSTANCE ti() AS STRING             'column header titles
   INSTANCE wi() AS LONG               'column header widths (neg for right alligned)
   INSTANCE colType() AS LONG          '1,2,3 for str, val, date column type (neg for right-adjust) 
   INSTANCE items() AS STRING          'row strings with TAB separated columns
   INSTANCE recNo() AS LONG            'record number as originally added

   CLASS METHOD showList
      LOCAL i, k AS LONG, s AS STRING 
      LISTBOX RESET hDlg, listID
      FOR i = 1 TO numItems
         s = items(recNo(i))
         IF recNo(i) = hiLite THEN hiLite = i
         REPLACE "," WITH $TAB IN s
         LISTBOX ADD hDlg, listID, s
      NEXT i 
      IF hiLite = 0 THEN hiLite = 1
      CONTROL SEND hDlg, %ID_LIST, %LB_SETCURSEL, hiLite - 1, 0
      showStatus 1: showStatus 2
      CONTROL SET FOCUS hDlg, %ID_LIST
   END METHOD
      
   INTERFACE IColumnList
      INHERIT IUNKNOWN

      PROPERTY GET getHandle() AS LONG 
         PROPERTY = hDlg 
      END PROPERTY

      PROPERTY GET getNumShown() AS LONG 
         PROPERTY = numItems 
      END PROPERTY
      
      PROPERTY GET getHiLite() AS LONG 
         PROPERTY = hiLite 
      END PROPERTY
      
      PROPERTY SET setHiLite(BYVAL n AS LONG)
         hiLite = n
      END PROPERTY
      
      METHOD getRecordNo(BYVAL i AS LONG) AS LONG
         IF UBOUND(recNo()) >= i THEN
            METHOD = recNo(i)
         END IF
      END METHOD
      
      METHOD createList(hParent AS LONG, ID_List AS LONG, _
                        a AS LONG, b AS LONG, w AS LONG, h AS LONG, _
                        pwi AS DWORD, pti AS DWORD, numCols AS LONG)
         'hParent, ID_List = handle of containing dialog, control id
         'a, b, w, h       = location and size of control including listbox headers (pixels/units from parent)
         'pwi              = pointer to wi(), width of columns (neg value for right adjusted)
         'pti              = pointer to ti(), title of columns (leading chr$(1) for value, chr$(2) for date)
         'numCols          = number of columns
         '-----------------------------------------------------------------------------------------------------     
         LOCAL nStyle, i, j, k, n, hdc, ws, ht, rx, ry, u, nPixels AS LONG
         LOCAL cSize AS SIZEL, r AS RECT, txt AS ASCIIZ*16, s AS STRING
         DIM wi(1 TO numCols) AT pwi
         DIM ti(1 TO numCols) AT pti
         DIM tabstops(numCols - 1) AS LONG
         DIM colType(1 TO numCols) AS INSTANCE LONG
         'determine pixels or dialog units------------------------- 
         hDlg = hParent: listID = ID_list 
         DIALOG GET SIZE hDlg TO rx, ry  
         GetWindowRect  hDlg, r
         IF rx = (r.nRight - r.nLeft) THEN nPixels = 1
         'determine header height and scroll width ----------------
         txt = "0": hdc = GetDC(hDlg)
         GetTextExtentPoint32 hdc, BYVAL VARPTR(txt), LEN(txt), cSize
         ht = 1.2 * cSize.cy: u = cSize.cx
         ws = GetSystemMetrics(%SM_CXVSCROLL)
         IF nPixels = 0 THEN DIALOG PIXELS hDlg, ws, ht TO UNITS ws, ht
         ReleaseDC hDlg, hdc
         'define column types -------------------------------------
         FOR i = 1 TO numCols 
            colType(i) = ASC(LEFT$(ti(i), 1)) + 1 
            IF colType(i) <= 3 THEN ti(i) = MID$(ti(i), 2) ELSE colType(i) = 1
            colType(i) = SGN(wi(i)) * coltype(i): wi(i) = ABS(wi(i)) 
         NEXT i
         'fit last column width -----------------------------------
         j = 0: FOR i = 1 TO numCols - 1: j = j + wi(i): NEXT i
         wi(numCols) = w - j - ws
         'create the listbox --------------------------------------
         x = a: y = b: xx = w: yy = h: numColumns = numCols
         nStyle = %WS_VSCROLL OR %LBS_DISABLENOSCROLL OR %LBS_USETABSTOPS OR %WS_TABSTOP
         CONTROL ADD LISTBOX, hDlg, listID, , x, y+ht, xx, yy-ht, nStyle, %WS_EX_CLIENTEDGE, CALL ListCB
         'define tabstops for listbox -----------------------------
         tabstops(0) = 2
         FOR i = 1 TO numColumns - 1: tabstops(i) = tabstops(i - 1) + wi(i): NEXT i
         tabstops(numColumns - 1) = tabstops(numColumns - 1) - 2
         FOR i = 0 TO numColumns - 1
            IF colType(i + 1) < 0 THEN 'right alligned tabstop
               tabstops(i) = SGN(colType(i + 1)) * (tabstops(i) + wi(i + 1) - 6)
            END IF
         NEXT i
         IF nPixels THEN 'convert pixels to dialog units for tabstops
            DIALOG PIXELS hDlg, 1000, 0 TO UNITS rx, ry
            FOR i = 0 TO numColumns - 1: tabstops(i) = rx/1000 * tabstops(i): NEXT i
         END IF
         CONTROL SEND hDlg, listID, %LB_SETTABSTOPS, numColumns, VARPTR(tabstops(0))
         'create listbox headers ----------------------------------
         n = %SS_NOTIFY
         j = x
         FOR i = 1 TO numColumns
            IF i > 1 THEN j = j + wi(i - 1)
            IF colType(i) < 0 THEN 
               s = ti(i) + "  ": nStyle = %SS_RIGHT + n 
            ELSE 
               s = "  " + ti(i): nStyle = %SS_LEFT + n
            END IF
            CONTROL ADD LABEL, hDlg, listID+i, s, j, y, wi(i), ht, nStyle, %WS_EX_STATICEDGE, CALL ListCB
         NEXT i
      END METHOD
      
      METHOD addItem(itemStr AS STRING, nShow AS LONG)
         'add itemStr to items() and list if nShow > 0
         INCR numItems
         REDIM PRESERVE items(1 TO numItems)
         REDIM PRESERVE recNo(1 TO numItems)
         items(numItems) = itemStr
         recNo(numItems) = numItems
         IF nShow THEN colList.showList
      END METHOD

      METHOD convertDate(s AS STRING) AS LONG
         'convert "10/13/1934" to 19341013 
         LOCAL mo AS WORD, day AS WORD, yr AS WORD
         mo = VAL(LEFT$(s, 2))
         day = VAL(MID$(s, 4, 2))
         yr = VAL(RIGHT$(s, 4)) - 1980
         ROTATE RIGHT yr, 7
         ROTATE LEFT mo, 5
         METHOD = yr + mo + day
      END METHOD
      
      METHOD columnSort(nCol AS LONG)
         'this routine toggles the column sort when the column header is clicked.
         'colType() holds the type of column: string=1, value=2, or date=3 
         'the key to this routine is simply to determine the tagged recNo()
         '------------------------------------------------------------------------
         LOCAL i, n, nSort AS LONG, s AS STRING
         DIM sArray(1 TO numItems) AS STRING
         DIM nArray(1 TO numItems) AS LONG
         hiLite = recNo(hiLite)
         n = ABS(colType(nCol)) 'neg colType signals right adjusted
         FOR i = 1 TO numItems 'fill the appropriate array with the specified column
            s = PARSE$(items(recNo(i)), nCol)
            IF n = 2 THEN 'value, use numeric array
               nArray(i) = VAL(s)
            ELSEIF n = 3 THEN 'date, use numeric array
               nArray(i) = colList.convertDate(s)
            ELSE 'use string array
               sArray(i) = s
            END IF   
         NEXT i
         nSort = -1
         IF sArray(numItems) > sArray(1) OR nArray(numItems) > nArray(1) THEN nSort = 1
         IF nSort < 0 THEN 'sort as ascending
            IF n = 1 THEN 'use string array
               ARRAY SORT sArray(), COLLATE UCASE, TAGARRAY recNo(), ASCEND
            ELSE 'use numeric array
               ARRAY SORT nArray(), TAGARRAY recNo(), ASCEND
            END IF 
         ELSE 'sort as descending
            IF n = 1 THEN 'use string array
               ARRAY SORT sArray(), COLLATE UCASE, TAGARRAY recNo(), DESCEND
            ELSE 'use numeric array
               ARRAY SORT nArray(), TAGARRAY recNo(), DESCEND
            END IF
         END IF
         ME.showList
      END METHOD
      
   END INTERFACE   
END CLASS

CALLBACK FUNCTION listCB()
   LOCAL top, k, n AS LONG
   SELECT CASE CB.MSG
   CASE %WM_COMMAND
      IF CB.CTL = %ID_List AND CB.CTLMSG = %LBN_SELCHANGE THEN
         CONTROL SEND CB.HNDL, %ID_List, %LB_GETTOPINDEX, 0, 0 TO k
         CONTROL SEND CB.HNDL, %ID_LIST, %LB_GETCURSEL, 0, 0 TO n 
         top = k + 1: colList.setHiLite() = n + 1
      ELSEIF CB.CTL > %ID_List AND CB.CTLMSG = %STN_CLICKED THEN
         colList.columnSort(CB.CTL - %ID_List)
      END IF
   END SELECT
END FUNCTION

FUNCTION PBMAIN()
   LOCAL hMain, nStyle, n, x, y AS LONG
   LOCAL title AS STRING
   LOCAL wi() AS LONG, ti() AS STRING
   LET colList = CLASS "CColumnList"
   n = 5
   DIM wi(1 TO n), ti(1 TO n)
   wi(1) = 50: wi(2) = 50: wi(3) = 70: wi(4) = 70: wi(5) = -60  'units
'   wi(1) = 70: wi(2) = 70: wi(3) = 100: wi(4) = 100: wi(5) = -90  'pixels
   ti(1) = CHR$(2) + "Date": ti(2) = "Type": ti(3) = "Descr": ti(4) = "Name": ti(5) = CHR$(1) + "Amount" 
   title = "Column List Demo"
   nStyle = %WS_SYSMENU OR %WS_MINIMIZEBOX
   DIALOG NEW 0, title$,,, 320, 350, nStyle TO hMain
'   DIALOG NEW PIXELS, 0, title$,,, 450, 350, nStyle TO hMain 
   colList.createList(hMain, %ID_List, 10, 20, 300, 300, VARPTR(wi(1)), VARPTR(ti(1)), 5)
'   colList.createList(hMain, %ID_List, 10, 20, 430, 300, VARPTR(wi(1)), VARPTR(ti(1)), 5)
   DIALOG GET SIZE hMain TO x, y 
   CONTROL ADD STATUSBAR, hMain, %ID_Status, "", 0, 0, 0, 0
   STATUSBAR SET PARTS hMain, %ID_Status, x - 90, 999
   DIALOG SHOW MODAL hMain CALL mainProc
END FUNCTION

CALLBACK FUNCTION mainProc()
   LOCAL i, numShown, yr, mo, day, amt AS LONG
   LOCAL sDate, sType, sDescr, sName, sAmt, sItem AS STRING
   SELECT CASE CBMSG
      CASE %WM_INITDIALOG
         numShown = 24: yr = 2000: mo = 6: day = 0: amt = 200
         FOR i = 1 TO numShown
            day = day + 5: IF day > 30 THEN INCR mo: day = 5
            IF mo > 12 THEN INCR yr: mo = 1: day = 5
            sDate = FORMAT$(mo, "00") + "/" + FORMAT$(day, "00") + "/" + FORMAT$(yr, "0000")
            sType = "type" + STR$(i MOD 6)
            sDescr = "description " + FORMAT$(i, "00")
            sName = "name " + FORMAT$(i MOD 16, "00")
            sAmt = TRIM$(STR$(amt + i)) + ".00"
            sItem = sDate + "," + sType + "," + sDescr + "," + sName + "," + sAmt
            colList.addItem(sItem, (i = numShown))
         NEXT i
      CASE %WM_COMMAND
         IF CB.CTL = %ID_List AND CB.CTLMSG = %LBN_SELCHANGE THEN
            showStatus 1: showStatus 2
         ELSEIF CB.CTL > %ID_List AND CB.CTL <= %ID_List + numShown THEN
            IF CB.CTLMSG = %STN_CLICKED THEN showStatus 1   
         END IF
   END SELECT   
END FUNCTION

SUB showStatus(panelNo AS LONG)
   LOCAL n, k, hDlg AS LONG, s AS STRING
   k = colList.getHiLite
   IF panelNo = 1 THEN 
      n = colList.getRecordNo(k)
      s = "Record number: " + STR$(n)
   ELSEIF panelNo = 2 THEN
      n = colList.getNumShown
      s = STR$(k)+" :"+STR$(n)
   END IF
   hDlg = colList.getHandle
   STATUSBAR SET TEXT hDlg, %ID_Status, panelNo, 0, s
END SUB