ListView vsReport 模式下,原生 SortType 僅支援第一欄 (Caption) 排序。需對子項目 (SubItems) 排序時,必須自行實作 OnColumnClick 事件處理。本文展示完整的奇數次降序、偶數次升序切換排序實作。
ViewStyle = vsReportSortType = stNone (關閉內建排序)
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; // 新項目索引
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 內部儲存格式:_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) 或 StrToDateTimeColumn->ImageIndex 顯示箭頭圖示掌握 ListView 子項目排序核心 — SubItems 文字解析 + 臨時項目交換 + static 方向記憶 — 即可為報表類介面提供專業級互動體驗。