首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 組合語言

ListView 控件完全掌握:報表模式、欄位排序、圖示切換與雙擊回應實戰

     編輯:組合語言

ListView (SysListView32) 核心能力

ListView 是 Common Controls 中最強大的列表呈現控件,支援四種檢視模式:

  • Large Icons (LVS_ICON):大圖示,適合資源管理器「大圖示」。
  • Small Icons (LVS_SMALLICON):小圖示。
  • List (LVS_LIST):單欄多行,自動換行排列。
  • Report/Details (LVS_REPORT):功能最完整,多欄表格,支援欄位標題點擊排序、子項目顯示。

本文聚焦 Report 模式,實作「列出當前目錄檔案 (檔名/大小)、點擊欄位標題排序、選單切換檢視模式、雙擊顯示檔名」完整範例。

四大建構步驟

  1. CreateWindowEx 建立控件,類別名 SysListView32,風格含 LVS_REPORT。
  2. 插入欄位:發送 LVM_INSERTCOLUMN + LV_COLUMN 結構 (標題、寬度、對齊)。
  3. 插入項目/子項目:LVM_INSERTITEM (主項目, iSubItem=0) + LVM_SETITEM (子項目, iSubItem>0)。
  4. 處理通知:WM_NOTIFY 接收 LVN_COLUMNCLICK (排序)、NM_DBLCLK (雙擊)。

核心結構體

LV_COLUMN STRUCT
    imask      DWORD ? ; 有效旗標: LVCF_TEXT, LVCF_WIDTH, LVCF_FMT, LVCF_SUBITEM
    fmt        DWORD ? ; 對齊: LVCFMT_LEFT, CENTER, RIGHT
    lx         DWORD ? ; 欄位寬度 (像素)
    pszText    DWORD ? ; 標題字串指標
    cchTextMax DWORD ? ; 查詢時緩衝區大小
    iSubItem   DWORD ? ; 關聯子項目索引
    iImage     DWORD ? ; 圖示索引
    iOrder     DWORD ? ; IE3+ 相容
LV_COLUMN ENDS

LV_ITEM STRUCT
    imask      DWORD ? ; LVIF_TEXT, LVIF_PARAM, LVIF_IMAGE, LVIF_STATE...
    iItem      DWORD ? ; 項目索引 (行號, 0-based)
    iSubItem   DWORD ? ; 子項目索引 (欄號, 0=主項目)
    state      DWORD ? ; 狀態旗標
    stateMask  DWORD ? ; 狀態遮罩
    pszText    DWORD ? ; 文字指標
    cchTextMax DWORD ? ; 緩衝區大小
    iImage     DWORD ? ; 圖示索引
    lParam     DWORD ? ; 應用自訂資料 (排序關鍵!)
    iIndent    DWORD ? ; 縮排
LV_ITEM ENDS

完整範例核心邏輯 (MASM32)

1. 建立控件與插入欄位

.data
ListViewClassName db "SysListView32",0
Heading1 db "Filename",0
Heading2 db "Size",0

.code
; WM_CREATE 中
invoke CreateWindowEx, NULL, addr ListViewClassName, NULL, \
    LVS_REPORT+WS_CHILD+WS_VISIBLE, 0,0,0,0, hWnd, NULL, hInstance, NULL
mov hList, eax

; InsertColumn 程序
InsertColumn proc
    LOCAL lvc:LV_COLUMN
    ; 欄位 0: Filename (左對齊, 150px)
    mov lvc.imask, LVCF_TEXT+LVCF_WIDTH
    mov lvc.pszText, offset Heading1
    mov lvc.lx, 150
    invoke SendMessage, hList, LVM_INSERTCOLUMN, 0, addr lvc

    ; 欄位 1: Size (右對齊, 100px) -> 需加 LVCF_FMT
    or lvc.imask, LVCF_FMT
    mov lvc.fmt, LVCFMT_RIGHT
    mov lvc.pszText, offset Heading2
    mov lvc.lx, 100
    invoke SendMessage, hList, LVM_INSERTCOLUMN, 1, addr lvc
    ret
InsertColumn endp

2. 列舉檔案並填入項目

FillFileInfo proc uses edi
    LOCAL finddata:WIN32_FIND_DATA
    LOCAL FHandle:DWORD

    invoke FindFirstFile, addr FileNamePattern, addr finddata  ; "*.*"
    .if eax != INVALID_HANDLE_VALUE
        mov FHandle, eax
        xor edi, edi                    ; edi = 行號 (iItem)
        .while eax != 0
            test finddata.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY
            .if ZERO?                   ; 略過目錄,只顯示檔案
                invoke ShowFileInfo, edi, addr finddata
                inc edi
            .endif
            invoke FindNextFile, FHandle, addr finddata
        .endw
        invoke FindClose, FHandle
    .endif
    ret
FillFileInfo endp

ShowFileInfo proc uses edi row:DWORD, lpFind:DWORD
    LOCAL lvi:LV_ITEM
    LOCAL buffer[20]:BYTE
    mov edi, lpFind
    assume edi:ptr WIN32_FIND_DATA

    ; --- 插入主項目 (檔名, 第 0 欄) ---
    mov lvi.imask, LVIF_TEXT+LVIF_PARAM
    push row
    pop lvi.iItem
    mov lvi.iSubItem, 0
    lea eax, [edi].cFileName
    mov lvi.pszText, eax
    push row
    pop lvi.lParam          ; lParam 存行號,供排序時查詢用
    invoke SendMessage, hList, LVM_INSERTITEM, 0, addr lvi

    ; --- 插入子項目 (大小, 第 1 欄) ---
    mov lvi.imask, LVIF_TEXT
    inc lvi.iSubItem        ; = 1
    invoke wsprintf, addr buffer, addr template, [edi].nFileSizeLow  ; template = "%lu"
    lea eax, buffer
    mov lvi.pszText, eax
    invoke SendMessage, hList, LVM_SETITEM, 0, addr lvi
    assume edi:nothing
    ret
ShowFileInfo endp

3. 排序核心:LVM_SORTITEMS + CompareFunc

點擊欄位標題觸發 LVN_COLUMNCLICK,呼叫 LVM_SORTITEMS 傳入比較函式指標。比較函式接收兩個項目的 lParam,透過 LVM_GETITEMTEXT 取得文字比較。

; WM_NOTIFY -> LVN_COLUMNCLICK 處理
.if [edi].code == LVN_COLUMNCLICK
    assume edi:ptr NM_LISTVIEW
    .if [edi].iSubItem == 1          ; 點擊 Size 欄
        .if SizeSortOrder == 0 || SizeSortOrder == 2
            invoke SendMessage, hList, LVM_SORTITEMS, 1, addr CompareFunc ; 升序
            invoke UpdatelParam
            mov SizeSortOrder, 1
        .else
            invoke SendMessage, hList, LVM_SORTITEMS, 2, addr CompareFunc ; 降序
            invoke UpdatelParam
            mov SizeSortOrder, 2
        .endif
    .else                            ; 點擊 Filename 欄
        .if FileNameSortOrder == 0 || FileNameSortOrder == 4
            invoke SendMessage, hList, LVM_SORTITEMS, 3, addr CompareFunc ; 升序
            invoke UpdatelParam
            mov FileNameSortOrder, 3
        .else
            invoke SendMessage, hList, LVM_SORTITEMS, 4, addr CompareFunc ; 降序
            invoke UpdatelParam
            mov FileNameSortOrder, 4
        .endif
    .endif
.endif

;--------------------------------------------------
; CompareFunc: 依 SortType (wParam) 決定比較邏輯
;--------------------------------------------------
CompareFunc proc uses edi lParam1:DWORD, lParam2:DWORD, SortType:DWORD
    LOCAL buffer[256]:BYTE
    LOCAL buffer1[256]:BYTE
    LOCAL lvi:LV_ITEM

    mov lvi.imask, LVIF_TEXT
    lea eax, buffer
    mov lvi.pszText, eax
    mov lvi.cchTextMax, 256

    .if SortType == 1          ; Size 升序
        mov lvi.iSubItem, 1
        invoke SendMessage, hList, LVM_GETITEMTEXT, lParam1, addr lvi
        invoke String2Dword, addr buffer
        mov edi, eax
        invoke SendMessage, hList, LVM_GETITEMTEXT, lParam2, addr lvi
        invoke String2Dword, addr buffer
        sub edi, eax           ; edi - eax < 0 表示第一個較小 -> 排前面
        mov eax, edi
    .elseif SortType == 2      ; Size 降序
        mov lvi.iSubItem, 1
        invoke SendMessage, hList, LVM_GETITEMTEXT, lParam1, addr lvi
        invoke String2Dword, addr buffer
        mov edi, eax
        invoke SendMessage, hList, LVM_GETITEMTEXT, lParam2, addr lvi
        invoke String2Dword, addr buffer
        sub eax, edi           ; 反向
    .elseif SortType == 3      ; Filename 升序 (不分大小寫)
        mov lvi.iSubItem, 0
        invoke SendMessage, hList, LVM_GETITEMTEXT, lParam1, addr lvi
        invoke lstrcpy, addr buffer1, addr buffer
        invoke SendMessage, hList, LVM_GETITEMTEXT, lParam2, addr lvi
        invoke lstrcmpi, addr buffer1, addr buffer
    .else                      ; SortType == 4 Filename 降序
        mov lvi.iSubItem, 0
        invoke SendMessage, hList, LVM_GETITEMTEXT, lParam1, addr lvi
        invoke lstrcpy, addr buffer1, addr buffer
        invoke SendMessage, hList, LVM_GETITEMTEXT, lParam2, addr lvi
        invoke lstrcmpi, addr buffer, addr buffer1
    .endif
    ret
CompareFunc endp

; 排序後必須更新所有項目 lParam 為新索引,否則下次排序索引錯誤
UpdatelParam proc uses edi
    LOCAL lvi:LV_ITEM
    invoke SendMessage, hList, LVM_GETITEMCOUNT, 0, 0
    mov edi, eax
    mov lvi.imask, LVIF_PARAM
    mov lvi.iSubItem, 0
    mov lvi.iItem, 0
    .while edi > 0
        push lvi.iItem
        pop lvi.lParam
        invoke SendMessage, hList, LVM_SETITEM, 0, addr lvi
        inc lvi.iItem
        dec edi
    .endw
    ret
UpdatelParam endp

4. 雙擊回應:取得焦點項目文字

.elseif [edi].code == NM_DBLCLK
    invoke ShowCurrentFocus

ShowCurrentFocus proc
    LOCAL lvi:LV_ITEM
    LOCAL buffer[256]:BYTE
    ; 取得具有焦點的項目索引
    invoke SendMessage, hList, LVM_GETNEXTITEM, -1, LVNI_FOCUSED
    mov lvi.iItem, eax
    mov lvi.iSubItem, 0
    mov lvi.imask, LVIF_TEXT
    lea eax, buffer
    mov lvi.pszText, eax
    mov lvi.cchTextMax, 256
    invoke SendMessage, hList, LVM_GETITEM, 0, addr lvi
    invoke MessageBox, 0, addr buffer, addr AppName, MB_OK
    ret
ShowCurrentFocus endp

5. 選單切換檢視模式

.elseif uMsg == WM_COMMAND
    .if lParam == 0
        invoke GetWindowLong, hList, GWL_STYLE
        and eax, not LVS_TYPEMASK          ; 清除舊檢視風格
        mov edx, wParam
        and edx, 0FFFFh                    ; 低字 = 新風格 (菜單 ID 即風格值)
        push edx
        or eax, edx
        invoke SetWindowLong, hList, GWL_STYLE, eax
        pop edx
        invoke CheckMenuRadioItem, hMenu, IDM_ICON, IDM_LIST, edx, MF_CHECKED
    .endif

技巧:菜單 ID 直接定義為風格常數 (IDM_ICON equ LVS_ICON 等),wParam 直接拿來用。

視窗大小自適應

.elseif uMsg == WM_SIZE
    mov eax, lParam
    mov edx, eax
    and eax, 0FFFFh        ; 寬度
    shr edx, 16            ; 高度
    invoke MoveWindow, hList, 0, 0, eax, edx, TRUE

進階技巧總結

需求關鍵 API/訊息備註
大量資料效能LVS_OWNERDATA (虛擬模式)不儲存資料於控件,透過 LVN_GETDISPINFO 按需提供,支援百萬列。
全選/反選LVM_SETITEMSTATE + LVIS_SELECTED配合 LVM_GETNEXTITEM 迴圈。
拖曳重排欄位LVM_SETCOLUMNORDERARRAY需 LVS_EX_HEADERDRAGDROP 擴充風格。
自訂繪製NM_CUSTOMDRAW回應 CDDS_ITEMPREPAINT 修改字體/顏色/背景。
圖示支援ImageList_Create + LVM_SETIMAGELIST同 TreeView 圖示列表建立流程。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved