class ListViewColumnSorter : IComparer
{
private readonly int _columnIndex;
private readonly SortOrder _order;
public ListViewColumnSorter(int columnIndex, SortOrder order)
{
_columnIndex = columnIndex;
_order = order;
}
public int Compare(object x, object y)
{
ListViewItem itemX = (ListViewItem)x;
ListViewItem itemY = (ListViewItem)y;
string textX = itemX.SubItems[_columnIndex].Text;
string textY = itemY.SubItems[_columnIndex].Text;
int result = String.Compare(textX, textY, StringComparison.CurrentCulture);
return _order == SortOrder.Descending ? -result : result;
}
}
private void listView1_ColumnClick(object sender, ColumnClickEventArgs e)
{
// 切換排序順序 (Tag 保存目前順序)
SortOrder newOrder = SortOrder.Ascending;
if (listView1.Columns[e.Column].Tag is SortOrder currentOrder)
{
newOrder = currentOrder == SortOrder.Ascending ? SortOrder.Descending : SortOrder.Ascending;
}
listView1.Columns[e.Column].Tag = newOrder;
// 重設其他欄 Tag (可選:單欄排序模式)
foreach (ColumnHeader col in listView1.Columns)
if (col.Index != e.Column) col.Tag = null;
listView1.ListViewItemSorter = new ListViewColumnSorter(e.Column, newOrder);
listView1.Sort();
}
private void AdjustLastColumnWidth()
{
if (listView1.Columns.Count == 0) return;
// 暫時移除事件避免遞迴觸發
listView1.ColumnWidthChanged -= ListView1_ColumnWidthChanged;
int totalWidth = listView1.ClientSize.Width;
int usedWidth = 0;
for (int i = 0; i < listView1.Columns.Count - 1; i++)
usedWidth += listView1.Columns[i].Width;
int lastWidth = Math.Max(50, totalWidth - usedWidth - SystemInformation.VerticalScrollBarWidth);
listView1.Columns[listView1.Columns.Count - 1].Width = lastWidth;
listView1.ColumnWidthChanged += ListView1_ColumnWidthChanged;
}
private void ListView1_ColumnWidthChanged(object sender, ColumnWidthChangedEventArgs e)
=> AdjustLastColumnWidth();
private void ListView1_SizeChanged(object sender, EventArgs e)
=> AdjustLastColumnWidth();
private void Form1_Shown(object sender, EventArgs e)
=> AdjustLastColumnWidth(); // 首次顯示調整
| 功能 | 實作要點 |
|---|---|
| 數值/日期排序 | IComparer 中嘗試 double.TryParse/DateTime.TryParse,失敗回退字串比較 |
| 多欄排序 | Shift+Click 累加排序鍵,IComparer 依優先序比較多欄 |
| 排序指示器圖示 | OwnerDraw 模式繪製 ▲/▼ 於 ColumnHeader |
| 虛擬模式 | 大量資料 (>1萬筆) 設定 VirtualMode=true,配合 RetrieveVirtualItem 事件 |
| 雙緩衝減少閃爍 | 反射設定 listView1.GetType().GetProperty("DoubleBuffered", BindingFlags.NonPublic|BindingFlags.Instance).SetValue(listView1, true); |
public partial class EnhancedListViewForm : Form
{
public EnhancedListViewForm()
{
InitializeComponent();
listView1.View = View.Details;
listView1.FullRowSelect = true;
listView1.ColumnClick += ListView1_ColumnClick;
listView1.ColumnWidthChanged += ListView1_ColumnWidthChanged;
listView1.SizeChanged += ListView1_SizeChanged;
this.Shown += Form1_Shown;
}
// ... 上述所有事件處理方法
}
透過 IComparer 與事件機制,ListView 即可具備專業級排序與自適應欄寬能力,大幅提升使用者體驗。