WinForms ListView(Details 檢視)預設不支援儲存格內編輯。本文示範繼承 ListView 攔截滾動訊息、搭配獨立 ComboBox 控制項,在使用者點擊第一欄時精準定位並顯示下拉選單,選取後寫回儲存格並隱藏。
ListView,覆寫 WndProc 攔截 WM_VSCROLL/WM_HSCROLL,捲動時將焦點移回 ListView 觸發 ComboBox 失焦隱藏。MyListView 與隱藏的 ComboBox(Visible=false)。
public class MyListView : ListView
{
private const int WM_HSCROLL = 0x114;
private const int WM_VSCROLL = 0x115;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_VSCROLL || m.Msg == WM_HSCROLL)
this.Focus(); // 強制 ComboBox 失焦
base.WndProc(ref m);
}
}
private ListViewItem _clickedItem;
private void myListView1_MouseUp(object sender, MouseEventArgs e)
{
_clickedItem = myListView1.GetItemAt(e.X, e.Y);
if (_clickedItem == null) return;
// 僅第一欄可編輯
if (e.X > myListView1.Columns[0].Width) return;
Rectangle bounds = _clickedItem.Bounds;
// 處理水平捲動導致欄位部分可見的情況
if (bounds.Left < 0) { /* 計算可見寬度、調整 X */ }
// 垂直位置加上 ListView.Top
bounds.Y += myListView1.Top;
bounds.X += myListView1.Left;
cbListViewCombo.Bounds = bounds;
cbListViewCombo.Text = _clickedItem.Text;
cbListViewCombo.Visible = true;
cbListViewCombo.BringToFront();
cbListViewCombo.Focus();
}
private void cbListViewCombo_SelectedIndexChanged(object sender, EventArgs e)
{
_clickedItem.Text = cbListViewCombo.Text;
cbListViewCombo.Visible = false;
}
private void cbListViewCombo_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)Keys.Escape)
{
cbListViewCombo.Text = _clickedItem.Text; // 還原
cbListViewCombo.Visible = false;
}
else if (e.KeyChar == (char)Keys.Enter)
cbListViewCombo.Visible = false;
}
private void myListView1_Click(object sender, EventArgs e)
=> cbListViewCombo.Visible = false; // 點擊其他處隱藏
| 挑戰 | 解決方案 |
|---|---|
| 水平捲動時第一欄部分隱藏 | 計算 bounds.Left、bounds.Width 修正 ComboBox.Bounds |
| 垂直捲動時 ComboBox 位置錯位 | bounds.Y += listView.Top 轉換為表單座標 |
| 捲動時 ComboBox 残留 | MyListView.WndProc 攔截 WM_VSCROLL/WM_HSCROLL 呼叫 Focus() |
| 鍵盤 Esc 取消編輯 | KeyPress 捕捉 Keys.Escape 還原文字並隱藏 |
e.X 判斷欄位索引,顯示不同編輯控制項(DateTimePicker、TextBox、NumericUpDown)。ComboBox.DataSource 綁定列表,DisplayMember/ValueMember 分離顯示與實值。Validating 事件檢查輸入合法性,不合法時 e.Cancel = true 保持編輯狀態。ObjectListView (開源) 或自行實作 IEmbeddedEditor 介面統一管理。DataGridViewComboBoxColumn、編輯模式、資料繫結、虛擬模式,功能完整得多,新專案優先考慮。此技巧適合「必須用 ListView、僅需簡單下拉編輯」的舊專案維護場景;新專案請直接使用 DataGridView。