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

ListView 多欄點擊欄標標題排序:自訂比較函數與子項目提取技巧

     編輯:關於C++

ListView vsReport 模式下,原生 SortType 僅支援第一欄 (Caption) 排序。需對子項目 (SubItems) 排序時,必須自行實作 OnColumnClick 事件處理。本文展示完整的奇數次降序、偶數次升序切換排序實作。

前置設定

  • ListView ViewStyle = vsReport
  • SortType = stNone (關閉內建排序)
  • 透過 Columns Editor 新增欄位,Items Editor 新增資料 (含 SubItems)

核心變數與邏輯


void __fastcall TForm1::ListView1ColumnClick(TObject *Sender, TListColumn *Column) {
    static bool descending = true;  // static 保持跨呼叫狀態
    descending = !descending;       // 奇數次 true(降序),偶數次 false(升序)
    
    int colIndex = Column->Index;   // 點擊的欄索引
    int itemCount = ListView1->Items->Count;
    
    // 新增臨時項目作為交換緩衝
    ListView1->Items->Add();
    int tempIndex = itemCount;  // 新項目索引

第一欄 (Caption) 排序專用邏輯


    if (colIndex == 0) {
        for (int i = 0; i < itemCount - 1; i++)
            for (int j = i + 1; j < itemCount; j++) {
                bool swapNeeded = descending
                    ? (ListView1->Items->Item[i]->Caption > ListView1->Items->Item[j]->Caption)
                    : (ListView1->Items->Item[i]->Caption < ListView1->Items->Item[j]->Caption);
                if (swapNeeded) {
                    ListView1->Items->Item[tempIndex] = ListView1->Items->Item[i];
                    ListView1->Items->Item[i] = ListView1->Items->Item[j];
                    ListView1->Items->Item[j] = ListView1->Items->Item[tempIndex];
                }
            }
        ListView1->Items->Delete(tempIndex);
        return;
    }

子項目 (SubItems) 提取函數

SubItems 內部儲存格式:_Sub1\r\nSub2\r\nSub3\r\n...,需解析第 n 個子項目:


#include 

AnsiString __stdcall GetSubItem(AnsiString str, int n) {
    // n: 0-based 子項目索引
    AnsiString result;
    int idx = 1, count = 0;
    while (idx <= str.Length()) {
        // 尋找下一個 \r\n
        int next = str.AnsiPos("\r\n", idx);
        if (next == 0) next = str.Length() + 1;
        if (count == n) {
            result = str.SubString(idx, next - idx);
            break;
        }
        idx = next + 2;  // 跳過 \r\n
        count++;
    }
    return result;
}

子項目排序邏輯


    // colIndex > 0 為子項目欄位
    int subIndex = colIndex - 1;  // SubItems 0-based
    for (int i = 0; i < itemCount - 1; i++)
        for (int j = i + 1; j < itemCount; j++) {
            AnsiString si = GetSubItem(ListView1->Items->Item[i]->SubItems->Text, subIndex);
            AnsiString sj = GetSubItem(ListView1->Items->Item[j]->SubItems->Text, subIndex);
            bool swapNeeded = descending
                ? (si > sj)
                : (si < sj);
            if (swapNeeded) {
                ListView1->Items->Item[tempIndex] = ListView1->Items->Item[i];
                ListView1->Items->Item[i] = ListView1->Items->Item[j];
                ListView1->Items->Item[j] = ListView1->Items->Item[tempIndex];
            }
        }
    ListView1->Items->Delete(tempIndex);
}

關鍵技術點總結

技術點說明
static bool保持排序方向跨事件呼叫狀態
臨時項目利用 ListView 內建項目作為交換緩衝,避免額外記憶體配置
Caption vs SubItems第一欄用 Caption 直接比較,其他欄需解析 SubItems 文字
SubItems 格式以 \r\n 分隔,需自行解析提取指定索引
字串比較AnsiString 運算子 >/< 為字典序,數值排序需轉型 (StrToInt/Float)
氣泡排序O(n²) 適合百筆內資料,大量資料建議複製到陣列 qsort 或用虛擬模式

進階擴展建議

  • 數值/日期排序:StrToInt(si) > StrToInt(sj) 或 StrToDateTime
  • 排序指示器:在 Column->Caption 前加 ▲/▼ 或設定 Column->ImageIndex 顯示箭頭圖示
  • 虛擬模式 (OwnerData):大量資料時不建立實際 Items,排序在資料源層完成
  • 多欄複合排序:Shift+點擊次要欄,維護排序鍵優先級陣列

掌握 ListView 子項目排序核心 — SubItems 文字解析 + 臨時項目交換 + static 方向記憶 — 即可為報表類介面提供專業級互動體驗。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved