'
' Version 4 (Virtual Listview - custom drawn)
'
' This version with drawn splitter bars uses virtual listviews
' AND custom drawing. The listviews should have the %LVS_OWNERDATA
' style. The data for the listviews are held in the DataArray and
' are provided to the listviews in the %LVN_GETDISPINFO part of
' the callback function. Custom drawing is used as well, so you
' can also as in version 3 provide the font, background color
' and foreground color for the listviews.
'
' The column headers in the lower panels are omitted as in version 3.
'
' The custom drawing and the virtual character of the listviews mean
' that this version is reacting a little bit slower. Version 2 is
' the fastest with the least flicker, I think.
'
' Best regards,

'
' Erik Christensen ---------- September 16, 2005
Code:
#COMPILE EXE #DIM ALL #REGISTER NONE ' #INCLUDE "WIN32API.INC" #INCLUDE "COMMCTRL.INC" '------------------------------------------------------------------------------ %IDD_DIALOG1 = 101 %IDC_SYSLISTVIEW32_1 = 1001 %IDC_SYSLISTVIEW32_2 = 1002 %IDC_SYSLISTVIEW32_3 = 1003 %IDC_SYSLISTVIEW32_4 = 1004 '------------------------------------------------------------------------------ FUNCTION SampleListView(BYVAL hDlg AS DWORD, BYVAL lID AS LONG, BYVAL lColCnt _ AS LONG, BYVAL lRowCnt AS LONG) AS LONG LOCAL lCol AS LONG LOCAL lRow AS LONG LOCAL hCtl AS DWORD LOCAL tLVC AS LV_COLUMN LOCAL tLVI AS LV_ITEM LOCAL szBuf AS ASCIIZ * 32 LOCAL lStyle AS LONG CONTROL HANDLE hDlg, lID TO hCtl lStyle = ListView_GetExtendedListViewStyle(hCtl) ListView_SetExtendedListViewStyle(hCtl, lStyle OR %LVS_EX_FULLROWSELECT _ OR %LVS_EX_GRIDLINES) ' Load column headers. tLVC.mask = %LVCF_FMT OR %LVCF_TEXT OR %LVCF_SUBITEM tLVC.fmt = %LVCFMT_LEFT tLVC.pszText = VARPTR(szBuf) szBuf = "Individual No." tLVC.iOrder = 0 ListView_InsertColumn(hCtl, 0, tLVC) FOR lCol = 1 TO lColCnt-1 szBuf = USING$("Variable #", lCol) tLVC.iOrder = lCol ListView_InsertColumn(hCtl, lCol, tLVC) NEXT lCol szBuf = "" tLVC.iOrder = lColCnt ListView_InsertColumn(hCtl, lCol, tLVC) ' size columns. FOR lCol = 0 TO lColCnt - 1 SendMessage(hCtl, %LVM_SETCOLUMNWIDTH, lCol, MAKLNG(110, 0)) NEXT lCol SendMessage(hCtl, %LVM_SETCOLUMNWIDTH, lColCnt, MAKLNG(500, 0)) ListView_DeleteAllItems hCtl ListView_SetItemCountEx hCtl, lRowCnt, %LVSICF_NOINVALIDATEALL END FUNCTION '------------------------------------------------------------------------------ SUB SynchronizeWindows(BYVAL h1 AS DWORD, BYVAL h2 AS DWORD, BYVAL h3 AS DWORD, _ BYVAL h4 AS DWORD, BYREF ColWidth() AS LONG, BYVAL Cols AS LONG) LOCAL rc1 AS RECT, rc2 AS RECT, i AS LONG, j AS INTEGER FOR i = 0 TO Cols j = SendMessage(h1, %LVM_GETCOLUMNWIDTH, i, 0) IF j <> ColWidth(i) THEN ColWidth(i) = j SendMessage h2, %LVM_SETCOLUMNWIDTH, i, MAKLNG(j, 0) SendMessage h3, %LVM_SETCOLUMNWIDTH, i, MAKLNG(j, 0) SendMessage h4, %LVM_SETCOLUMNWIDTH, i, MAKLNG(j, 0) END IF NEXT rc1.nLeft = %LVIR_BOUNDS : SendMessage h1, %LVM_GETITEMRECT, 0, VARPTR(rc1) rc2.nLeft = %LVIR_BOUNDS : SendMessage h2, %LVM_GETITEMRECT, 0, VARPTR(rc2) SendMessage h2, %LVM_SCROLL, 0, rc2.nTop - rc1.nTop rc2.nLeft = %LVIR_BOUNDS : SendMessage h3, %LVM_GETITEMRECT, 0, VARPTR(rc2) SendMessage h3, %LVM_SCROLL, rc2.Nleft - rc1.nLeft, 0 END SUB '------------------------------------------------------------------------------ SUB SizeWindows(BYVAL h1 AS DWORD, BYVAL h2 AS DWORD, BYVAL h3 AS DWORD, BYVAL h4 AS DWORD, _ BYVAL x AS LONG, BYVAL y AS LONG, BYVAL xMid AS LONG, BYVAL yMid AS LONG) MoveWindow h1, 3, 3, xMid-6, yMid-6, %TRUE MoveWindow h2, xMid+3, 3, x-xMid-6, yMid-6, %TRUE MoveWindow h3, 3, yMid+3, xMid-6, y-yMid-6, %TRUE MoveWindow h4, xMid+3, yMid+3, x-xMid-6, y-yMid-6, %TRUE END SUB '------------------------------------------------------------------------------ SUB DrawSplitter(BYVAL hBr AS LONG, BYVAL FV AS LONG, BYVAL FH AS LONG, BYREF Ci AS LONG, BYVAL x AS LONG, _ BYVAL y AS LONG, BYVAL xMid AS LONG, BYVAL yMid AS LONG, BYVAL hWnd AS LONG) LOCAL rc AS RECT, hDC AS LONG, pt AS POINTAPI DIM co(1 TO 4) AS STATIC LONG hDC = GetDC(%HWND_DESKTOP) SelectObject hDC, hBr IF Ci = 1 THEN PatBlt hDC, co(1), co(2), co(3), co(4), %PATINVERT ClientToScreen hWnd, pt IF FH = 1 THEN ' horizontal movement of vertical splitter rc.nTop = 3 : rc.nBottom = y-3 : rc.nLeft = xMid-3 : rc.nRight = xMid+3 ELSEIF FV = 1 THEN ' vertical movement of horizontal splitter rc.nTop = yMid-3 : rc.nBottom = yMid+3 : rc.nLeft = 3 : rc.nRight = x-3 END IF co(1) = rc.nLeft + pt.x : co(2) = rc.nTop + pt.y : co(3) = rc.nRight - rc.nLeft : co(4) = rc.nBottom - rc.nTop PatBlt hDC, co(1), co(2), co(3), co(4), %PATINVERT ReleaseDC hWnd, hDC Ci = 1 END SUB '------------------------------------------------------------------------------ CALLBACK FUNCTION ShowDIALOG1Proc() STATIC x AS LONG, y AS LONG, xMid AS LONG, yMid AS LONG STATIC xPos AS LONG, yPos AS LONG STATIC h1 AS DWORD, h2 AS DWORD, h3 AS DWORD, h4 AS DWORD STATIC rc AS RECT STATIC PrevTime AS DOUBLE DIM ColWidth(0 TO 10) AS STATIC LONG STATIC i AS LONG, j AS LONG STATIC HorzFlag AS LONG, VertFlag AS LONG LOCAL pnmh AS NMHDR PTR LOCAL lpLVDispInfo AS LV_DISPINFO PTR LOCAL pnm AS NMLVCUSTOMDRAW PTR LOCAL szString AS ASCIIZ * 256 STATIC hDC AS LONG LOCAL memDC AS LONG LOCAL hbitmap AS LONG STATIC hBrush AS LONG STATIC hBrush2 AS LONG LOCAL oldBmp AS LONG STATIC Ci AS LONG STATIC hFatFont AS LONG, hOldFont AS LONG LOCAL LogPixelsY AS LONG, FontTypeSize AS LONG, FH AS LONG DIM DataArray(0 TO 10, 0 TO 60) AS STATIC STRING ' SELECT CASE AS LONG CBMSG CASE %WM_INITDIALOG ' SystemParametersInfo %SPI_GETWORKAREA, 0, BYVAL VARPTR(rc), 0 MoveWindow CBHNDL,rc.nLeft, rc.nTop,rc.nRight - rc.nLeft,rc.nBottom - rc.nTop,%TRUE DIALOG SET COLOR CBHNDL, 0, RGB(160,200,200) ' CONTROL ADD "SysListView32", CBHNDL, %IDC_SYSLISTVIEW32_1, _ "SysListView32_1", 0, 0, 0, 0, %WS_CHILD OR %WS_VISIBLE OR _ %WS_TABSTOP OR %LVS_REPORT OR %LVS_SHOWSELALWAYS OR %LVS_OWNERDATA, %WS_EX_CLIENTEDGE _ OR %WS_EX_LEFT OR %WS_EX_RIGHTSCROLLBAR CONTROL ADD "SysListView32", CBHNDL, %IDC_SYSLISTVIEW32_2, _ "SysListView32_2", 0, 0, 0, 0, %WS_CHILD OR %WS_VISIBLE OR _ %WS_TABSTOP OR %LVS_REPORT OR %LVS_SHOWSELALWAYS OR %LVS_OWNERDATA, %WS_EX_LEFT OR _ %WS_EX_CLIENTEDGE OR %WS_EX_RIGHTSCROLLBAR CONTROL ADD "SysListView32", CBHNDL, %IDC_SYSLISTVIEW32_3, _ "SysListView32_3", 0, 0, 0, 0, %WS_CHILD OR %WS_VISIBLE OR %LVS_NOCOLUMNHEADER OR _ %WS_TABSTOP OR %LVS_REPORT OR %LVS_SHOWSELALWAYS OR %LVS_OWNERDATA, %WS_EX_LEFT OR _ %WS_EX_CLIENTEDGE OR %WS_EX_RIGHTSCROLLBAR CONTROL ADD "SysListView32", CBHNDL, %IDC_SYSLISTVIEW32_4, _ "SysListView32_4", 0, 0, 0, 0, %WS_CHILD OR %WS_VISIBLE OR %LVS_NOCOLUMNHEADER OR _ %WS_TABSTOP OR %LVS_REPORT OR %LVS_SHOWSELALWAYS OR %LVS_OWNERDATA, %WS_EX_LEFT OR _ %WS_EX_CLIENTEDGE OR %WS_EX_RIGHTSCROLLBAR ' CONTROL HANDLE CBHNDL, %IDC_SYSLISTVIEW32_1 TO h1 CONTROL HANDLE CBHNDL, %IDC_SYSLISTVIEW32_2 TO h2 CONTROL HANDLE CBHNDL, %IDC_SYSLISTVIEW32_3 TO h3 CONTROL HANDLE CBHNDL, %IDC_SYSLISTVIEW32_4 TO h4 ' GetClientRect CBHNDL, rc x = rc.nRight : y = rc.nBottom xMid = x \ 2 : yMid = y \ 2 ' SizeWindows(h1, h2, h3, h4, x, y, xMid, yMid) ' SampleListView CBHNDL, %IDC_SYSLISTVIEW32_1, 11, 60 SampleListView CBHNDL, %IDC_SYSLISTVIEW32_2, 11, 60 SampleListView CBHNDL, %IDC_SYSLISTVIEW32_3, 11, 60 SampleListView CBHNDL, %IDC_SYSLISTVIEW32_4, 11, 60 ' FOR i = 0 TO 10 : ColWidth(i) = SendMessage(h1, %LVM_GETCOLUMNWIDTH, i, 0) : NEXT ' ' Make data FOR i = 0 TO 10 ' Columns FOR j = 0 TO 59 ' Rows IF i = 0 THEN DataArray(i,j) = FORMAT$(j + 1) ELSE DataArray(i,j) = FORMAT$(RND(100, 999)) END IF NEXT NEXT ' hDC = GetDC(%HWND_DESKTOP) : LogPixelsY = GetDeviceCaps(hDC, %LOGPIXELSY) FontTypeSize = 8 : FH = -MulDiv(FontTypeSize, LogPixelsY, 72) hFatFont = CreateFont(FH,0,0,0,700,0,0,0,0,3,2,1,82,"MS Sans Serif") ' ' Set column header font SendMessage ListView_GetHeader(h1), %WM_SETFONT, hFatFont, MAKLNG(%TRUE,0) SendMessage ListView_GetHeader(h2), %WM_SETFONT, hFatFont, MAKLNG(%TRUE,0) ' ' Make pitmap pattern for splitter. hDC = GetDC(%HWND_DESKTOP) memDC = CreateCompatibleDC(hDC) hBitmap = CreateCompatibleBitmap(hDC, 8, 8) oldBmp = SelectObject(memDC, hBitmap) rc.nTop = 0 : rc.nLeft = 0 : rc.nRight = 8 : rc.nBottom = 8 hBrush = GetStockObject(%BLACK_BRUSH) FillRect memDC, rc, hBrush ' make the pattern FOR j = 0 TO 7 STEP 1 IF j=0 OR j=2 OR j=4 OR j=6 THEN FOR i = 0 TO 7 STEP 2 SetPixel memDC,i, j, RGB(255,255,255) NEXT ELSE FOR i = 1 TO 7 STEP 2 SetPixel memDC, i, j,RGB(255,255,255) NEXT END IF NEXT 'Create the pattern brush hBrush2 = CreatePatternBrush(hBitmap) DeleteDC memDC DeleteObject hBitmap hBrush = CreateSolidBrush(RGB(160,200,200)) ReleaseDC CBHNDL, hDC ' release desktop DC hDC = GetDC(CBHNDL) ' device context of dialog Ci = 0 ' flag used in SUB DrawSplitter ' CASE %WM_NOTIFY pnmh = CBLPARAM SELECT CASE @pnmh.code CASE %NM_CUSTOMDRAW ' Here you can specify font and color. pnm = CBLPARAM SELECT CASE @pnm.nmcd.dwDrawStage CASE %CDDS_PREPAINT ' The following statement ensures that the relevant ' messages are coming back to Windows, so Windows ' can respond appropriately. This method may be useful ' in many other situations where the DDT engine may ' swallow the information. ' The following statement makes Windows happy: SetWindowLong CBHNDL,%DWL_MSGRESULT,%CDRF_NOTIFYITEMDRAW ' This statement makes the DDT engine happy: FUNCTION = 1: EXIT FUNCTION CASE %CDDS_ITEMPREPAINT SetWindowLong CBHNDL,%DWL_MSGRESULT,%CDRF_NOTIFYSUBITEMDRAW FUNCTION = 1: EXIT FUNCTION CASE %CDDS_SUBITEM OR %CDDS_ITEMPREPAINT IF @pnm.iSubItem = 0 THEN ' "Row header" ' Specify background color @pnm.clrTextBk = RGB(227,227,227) ' light gray ' Specify text color @pnm.clrText = %BLACK ' Specify font hOldFont = SelectObject(@pnm.nmcd.hdc, hFatFont) ELSE SelectObject @pnm.nmcd.hdc, hOldFont IF (@pnm.nmcd.dwItemSpec MOD 2) THEN @pnm.clrTextBk = RGB(200,255,255) @pnm.clrText = %BLACK ELSE @pnm.clrTextBk = %WHITE @pnm.clrText = %BLACK END IF END IF SetWindowLong CBHNDL,%DWL_MSGRESULT,(%CDRF_NEWFONT OR %CDRF_NOTIFYSUBITEMDRAW) FUNCTION = 1: EXIT FUNCTION END SELECT ' CASE %LVN_GETDISPINFO 'Virtual ListView ask for Item text lpLVDispInfo = CBLPARAM IF (@lpLVDispInfo.item.mask AND %LVIF_TEXT) THEN ' Specify text to be used szString = DataArray(@lpLVDispInfo.item.iSubItem, @lpLVDispInfo.item.iItem) @lpLVDispInfo.item.pszText = VARPTR(szString) END IF SELECT CASE @pnmh.idFrom CASE %IDC_SYSLISTVIEW32_1 : PostMessage CBHNDL, %WM_USER + 401, 0, 0 CASE %IDC_SYSLISTVIEW32_2 : PostMessage CBHNDL, %WM_USER + 402, 0, 0 CASE %IDC_SYSLISTVIEW32_3 : PostMessage CBHNDL, %WM_USER + 403, 0, 0 CASE %IDC_SYSLISTVIEW32_4 : PostMessage CBHNDL, %WM_USER + 404, 0, 0 END SELECT END SELECT ' CASE %WM_USER + 401 IF TIMER - PrevTime > 0.15 AND GetFocus <> h1 THEN SetFocus h1 PrevTime = TIMER IF GetFocus = h1 THEN SynchronizeWindows h1, h2, h3, h4, ColWidth(), 10 ' CASE %WM_USER + 402 IF TIMER - PrevTime > 0.15 AND GetFocus <> h2 THEN SetFocus h2 PrevTime = TIMER IF GetFocus = h2 THEN SynchronizeWindows h2, h1, h4, h3, ColWidth(), 10 ' CASE %WM_USER + 403 IF TIMER - PrevTime > 0.15 AND GetFocus <> h3 THEN SetFocus h3 PrevTime = TIMER IF GetFocus = h3 THEN SynchronizeWindows h3, h4, h1, h2, ColWidth(), 10 ' CASE %WM_USER + 404 IF TIMER - PrevTime > 0.15 AND GetFocus <> h4 THEN SetFocus h4 PrevTime = TIMER IF GetFocus = h4 THEN SynchronizeWindows h4, h3, h2, h1, ColWidth(), 10 ' CASE %WM_SIZE x = LOWRD(CBLPARAM) : y = HIWRD(CBLPARAM) SizeWindows h1, h2, h3, h4, x, y, xMid, yMid ' CASE %WM_MOUSEMOVE xPos = LOWRD(CBLPARAM) : yPos = HIWRD(CBLPARAM) IF xPos > 8 AND xPos < x-8 AND yPos > 8 AND yPos < y-8 THEN IF ISFALSE (CBWPARAM AND %MK_LBUTTON) THEN ' left button not down IF ABS(xPos - xMid) < 7 AND yPos > 5 AND yPos < y-5 AND ABS(yPos - yMid) > 5 THEN MOUSEPTR 9 : VertFlag = 0 : HorzFlag = 1 IF ABS(yPos - yMid) < 7 AND xPos > 4 AND xPos < x-5 AND ABS(xPos - xMid) > 5 THEN MOUSEPTR 7 : HorzFlag = 0 : VertFlag = 1 ELSE ' left button down - dragging is going on IF HorzFlag = 1 THEN MOUSEPTR 9 : xMid = xPos : DrawSplitter hBrush2, VertFlag, HorzFlag, Ci, x, y, xMid, yMid, CBHNDL IF VertFlag = 1 THEN MOUSEPTR 7 : yMid = yPos : DrawSplitter hBrush2, VertFlag, HorzFlag, Ci, x, y, xMid, yMid, CBHNDL END IF END IF ' CASE %WM_LBUTTONDOWN IF HorzFlag = 1 OR VertFlag = 1 THEN SetCapture CBHNDL ELSE ReleaseCapture xPos = LOWRD(CBLPARAM) : yPos = HIWRD(CBLPARAM) IF xPos > 8 AND xPos < x-8 AND yPos > 8 AND yPos < y-8 THEN IF HorzFlag = 1 THEN MOUSEPTR 9 : xMid = xPos : DrawSplitter hBrush2, VertFlag, HorzFlag, Ci, x, y, xMid, yMid, CBHNDL IF VertFlag = 1 THEN MOUSEPTR 7 : yMid = yPos : DrawSplitter hBrush2, VertFlag, HorzFlag, Ci, x, y, xMid, yMid, CBHNDL END IF ' CASE %WM_LBUTTONUP SizeWindows h1, h2, h3, h4, x, y, xMid, yMid ReleaseCapture : Ci = 0 ' Ensure overpainting of splitter bar IF HorzFlag = 1 THEN rc.nTop = 3 : rc.nBottom = y-3 : rc.nLeft = xMid-3 : rc.nRight = xMid+3 IF VertFlag = 1 THEN rc.nTop = yMid-3 : rc.nBottom = yMid+3 : rc.nLeft = 3 : rc.nRight = x-3 FillRect hDC, rc, hBrush HorzFlag = 0 : VertFlag = 0 InvalidateRect CBHNDL, BYVAL %NULL, %FALSE ' CASE %WM_DESTROY DeleteObject hBrush DeleteObject hBrush2 DeleteObject hFatFont ReleaseDC CBHNDL, hDC ' END SELECT END FUNCTION '------------------------------------------------------------------------------ FUNCTION PBMAIN() LOCAL hDlg AS DWORD LOCAL CC1 AS INIT_COMMON_CONTROLSEX CC1.dwSize=SIZEOF(CC1) CC1.dwICC=%ICC_WIN95_CLASSES InitCommonControlsEX CC1 DIALOG NEW %HWND_DESKTOP, "Listview with movable vertical and horizontal splitter windows", _ 0, 0, 0, 0, %WS_OVERLAPPED OR %WS_BORDER OR _ %WS_DLGFRAME OR %WS_THICKFRAME OR %WS_CAPTION OR %WS_SYSMENU OR _ %WS_MINIMIZEBOX OR %WS_CLIPSIBLINGS OR %WS_VISIBLE OR %DS_MODALFRAME _ OR %DS_3DLOOK OR %DS_NOFAILCREATE OR %DS_SETFONT, %WS_EX_CLIENTEDGE _ OR %WS_EX_CONTROLPARENT OR %WS_EX_LEFT OR %WS_EX_LTRREADING OR _ %WS_EX_RIGHTSCROLLBAR, TO hDlg DIALOG SHOW MODAL hDlg, CALL ShowDIALOG1Proc END FUNCTION
[This message has been edited by Erik Christensen (edited September 16, 2005).]
Leave a comment: