功能特色
- 點擊欄位標題切換升冪/降冪排序
- 支援數字、IP 位址、字串三種資料型別智慧排序
- 可重複使用的輔助類別設計
- 零修改整合現有 ListView
核心程式碼:ListViewHelper.cs
using System;
using System.Collections;
using System.Windows.Forms;
using System.Text.RegularExpressions;
namespace Common
{
/// ListView 排序輔助類別
public class ListViewHelper
{
/// 掛載到 ListView 的 ColumnClick 事件
public static void ListView_ColumnClick(object sender, ColumnClickEventArgs e)
{
ListView lv = sender as ListView;
var sorter = lv.ListViewItemSorter as ListViewColumnSorter;
if (sorter == null) return; // 未初始化排序器
// 同一欄位:切換升/降冪
if (e.Column == sorter.SortColumn)
{
sorter.Order = (sorter.Order == SortOrder.Ascending)
? SortOrder.Descending : SortOrder.Ascending;
}
// 不同欄位:預設升冪
else
{
sorter.SortColumn = e.Column;
sorter.Order = SortOrder.Ascending;
}
lv.Sort(); // 觸發排序
}
}
/// 實作 IComparer 的欄位排序器
public class ListViewColumnSorter : IComparer
{
private int columnToSort = 0;
private SortOrder orderOfSort = SortOrder.None;
private CaseInsensitiveComparer objectCompare = new CaseInsensitiveComparer();
public int SortColumn { get => columnToSort; set => columnToSort = value; }
public SortOrder Order { get => orderOfSort; set => orderOfSort = value; }
public int Compare(object x, object y)
{
ListViewItem itemX = x as ListViewItem;
ListViewItem itemY = y as ListViewItem;
string textX = itemX.SubItems[columnToSort].Text;
string textY = itemY.SubItems[columnToSort].Text;
int compareResult;
// 1. IP 位址排序
if (IsIP(textX) && IsIP(textY))
{
compareResult = CompareIP(textX, textY);
}
// 2. 數字排序
else if (int.TryParse(textX, out int numX) && int.TryParse(textY, out int numY))
{
compareResult = CompareInt(numX, numY);
}
// 3. 字串排序 (不區分大小寫)
else
{
compareResult = objectCompare.Compare(textX, textY);
}
// 依排序方向調整結果
if (orderOfSort == SortOrder.Ascending) return compareResult;
if (orderOfSort == SortOrder.Descending) return -compareResult;
return 0;
}
// IP 格式驗證 (0.0.0.0 ~ 255.255.255.255)
private bool IsIP(string ip)
{
return Regex.IsMatch(ip, @"^(25[0-5]|2[0-4]\d|[01]?\d?\d)(\.(25[0-5]|2[0-4]\d|[01]?\d?\d)){3}$");
}
private int CompareInt(int x, int y) => x.CompareTo(y);
private int CompareIP(string ipX, string ipY)
{
var partsX = ipX.Split('.');
var partsY = ipY.Split('.');
for (int i = 0; i < 4; i++)
{
int cmp = int.Parse(partsX[i]).CompareTo(int.Parse(partsY[i]));
if (cmp != 0) return cmp;
}
return 0;
}
}
}
使用方式:在 Form_Load 中啟用
private void Form1_Load(object sender, EventArgs e)
{
// 1. 指定排序器
this.listView1.ListViewItemSorter = new Common.ListViewColumnSorter();
// 2. 掛載欄位點擊事件
this.listView1.ColumnClick += Common.ListViewHelper.ListView_ColumnClick;
}
ListView 設計時屬性建議
View = View.Details (詳細檢視模式)
FullRowSelect = true (整列選取)
GridLines = true (顯示分隔線)
Columns 預先加入所有欄位
擴充建議
| 需求 | 實作方向 |
| 日期時間排序 | 加入 DateTime.TryParse 判斷邏輯 |
| 浮點數排序 | 加入 double.TryParse |
| 自訂排序規則 | 暴露 Func<string, string, int> 委派屬性 |
| 排序指示器圖示 | OwnerDraw 模式繪製升/降冪箭頭 |
| 多欄位排序 | Shift+點擊支援次要排序鍵 |
原理說明
ListView.Sort() 內部呼叫 ListViewItemSorter.Compare() 進行比較
IComparer.Compare 回傳:< 0 (x 0 (x>y)
- IP 排序採用逐段數值比較,避免字串比較導致 "192.168.1.10" < "192.168.1.2" 的錯誤
CaseInsensitiveComparer 提供不區分大小寫的字串比較