ListView(Details 檢視)原生不支援單元格編輯。經典解法:在滑鼠點擊單元格時,動態定位一個隱藏的 ComboBox 覆蓋該單元格,編輯完成後寫回 ListViewItem。本文完整呈現自訂 MyListView 控件與表單代碼,詳細解析座標計算、事件處理、焦點管理等關鍵技術點。
ListView 的自訂控件(原始碼未提供,假設為標準 ListView)Visible=false、項目預填「NC」、「WA」View=Details、FullRowSelect=true、兩欄(State Abbr.、State)private void myListView1_MouseUp(object sender, MouseEventArgs e)
{
lvItem = myListView1.GetItemAt(e.X, e.Y);
if (lvItem == null) return;
// 僅第一欄可編輯
if (e.X > myListView1.Columns[0].Width) return;
var itemBounds = lvItem.Bounds;
// 處理水平捲動導致項目部分可見的邊界修正
if ((itemBounds.Left + myListView1.Columns[0].Width) < 0) return; // 完全捲出左側
else if (itemBounds.Left < 0) // 部分捲出左側
{
if ((itemBounds.Left + myListView1.Columns[0].Width) > myListView1.Width)
{
itemBounds.Width = myListView1.Width;
itemBounds.X = 0;
}
else
{
itemBounds.Width = myListView1.Columns[0].Width + itemBounds.Left;
itemBounds.X = 2;
}
}
else if (myListView1.Columns[0].Width > myListView1.Width)
{
itemBounds.Width = myListView1.Width;
}
else
{
itemBounds.Width = myListView1.Columns[0].Width;
itemBounds.X = 2;
}
// 轉換為表單座標
itemBounds.Y += myListView1.Top;
itemBounds.X += myListView1.Left;
// 套用至 ComboBox 並顯示
cbListViewCombo.Bounds = itemBounds;
cbListViewCombo.Text = lvItem.Text;
cbListViewCombo.Visible = true;
cbListViewCombo.BringToFront();
cbListViewCombo.Focus();
}
private void cbListViewCombo_SelectedValueChanged(object sender, EventArgs e)
{
lvItem.Text = cbListViewCombo.Text;
cbListViewCombo.Visible = false;
}
private void cbListViewCombo_KeyPress(object sender, KeyPressEventArgs e)
{
switch (e.KeyChar)
{
case (char)Keys.Escape:
cbListViewCombo.Text = lvItem.Text; // 還原原值
cbListViewCombo.Visible = false;
break;
case (char)Keys.Enter:
cbListViewCombo.Visible = false;
break;
}
}
private void myListView1_Click(object sender, EventArgs e)
{
cbListViewCombo.Visible = false;
}
ListView 座標系 (client) 表單座標系 ┌─────────────────────┐ ┌─────────────────────┐ │ [Header] │ │ │ │ ┌──────┬────────┐ │ Item.Bounds (相對 ListView) │ │ │Col 0 │ Col 1 │ │ → 需 + ListView.Left/Top │ │ │ Item │ │ │ → 修正水平捲動截斷 │ │ └──────┴────────┘ │ → 設定 ComboBox.Bounds │ └─────────────────────┘ └─────────────────────┘
| 技術點 | 實現方式 |
|---|---|
| 單元格編輯觸發 | MouseUp + GetItemAt + 欄位索引判斷 |
| ComboBox 定位 | Bounds 直接賦值 Rectangle(含座標轉換與捲動修正) |
| 焦點管理 | BringToFront() + Focus() 確保鍵盤輸入生效 |
| 值同步 | SelectedValueChanged 寫回 ListViewItem.Text |
| 取消編輯 | Escape 鍵還原原值並隱藏 |
| 失焦隱藏 | ListView.Click 隱藏 ComboBox |
DataGridViewComboBoxColumn,無需手工座標計算。DataGrid、ListView 皆支援 CellTemplate/CellEditingTemplate 雙模板原生編輯。ListView 嵌入 ComboBox 編輯是 WinForms 經典技巧,核心在於 精確的座標轉換與捲動修正、焦點與事件流程控制。雖代碼量較大,但理解其原理有助於掌握 WinForms 底層繪製與座標系統。新專案建議直接使用 DataGridView 或現代 UI 框架,避免重複造輪子。