希望是在 ListView 中双击某单元格时,该单元格内容可编辑。
不过 ListView 本身是不带这个功能的,我们要实现的原理是获取双击的是哪个单元格,然后在该单元格显示一个文本框(也可以是其他控件,本文以文本框为例)。
ListViewCell
namespace Yulin.YWinForm
{
public class ListViewCell
{
/// <summary>
/// 单元格所在的行。
/// </summary>
public ListViewItem Item { get; set; }
/// <summary>
/// Item 的 Index 值会变成 -1,暂时未找到原因,用这个代替。
/// </summary>
public int ItemIndex { get; set; }
/// <summary>
/// 单元格所在的列。
/// </summary>
public ColumnHeader Column { get; set; }
/// <summary>
/// 单元格相对于 ListView 的大小和位置。
/// </summary>
public Rectangle Bounds { get; set; }
}
public class ListViewCellLocator
{
[DllImport("user32")]
public static extern int GetScrollPos(int hwnd, int nBar);
/// <summary>
/// 根据位置 x、y 获得 ListViewCell。
/// </summary>
/// <param name="listView"></param>
/// <param name="x">工作区坐标表示的 x 轴坐标。</param>
/// <param name="y">工作区坐标表示的 y 轴坐标。</param>
/// <returns></returns>
public static ListViewCell GetCell(ListView listView, int x, int y)
{
ListViewCell cell = new ListViewCell();
// 获得单元格所在的行。
cell.Item = listView.GetItemAt(x, y);
if (cell.Item == null)
{
return null;
}
cell.ItemIndex = cell.Item.Index; // 现在 Item.Index 还能用
// 根据各列宽度,获得单元格所在的列,以及 Bounds。
int currentX = cell.Item.GetBounds(ItemBoundsPortion.Entire).Left; // 依次循环各列,表示各列的起点值
int scrollLeft = GetScrollPos(listView.Handle.ToInt32(), 0); // 可能出现了横向滚动条,左边隐藏起来的宽度
for (int i = 0; i < listView.Columns.Count; i++)
{
if (scrollLeft + x >= currentX &&
scrollLeft + x < currentX + listView.Columns[i].Width)
{
cell.Column = listView.Columns[i]; // 列找到了
Rectangle itemBounds = cell.Item.GetBounds(ItemBoundsPortion.Entire);
cell.Bounds = new Rectangle(currentX,
itemBounds.Y,
listView.Columns[i].Width,
itemBounds.Height);
break;
}
currentX += listView.Columns[i].Width;
}
if (cell.Column == null)
{
return null;
}
return cell;
}
}
}
<span style="font-family: SimSun, serif; font-size: 14px; line-height: 21px; text-indent: 28px;"><strong>EditableListView</strong></span>
<span style="font-family: SimSun, serif; font-size: 14px; line-height: 21px; text-indent: 28px;"></span><pre name="code" class="csharp">namespace Yulin.YWinForm
{
public class EditableListViewSubmittingEventArgs : EventArgs
{
public ListViewCell Cell { get; set; }
public string Value { get; set; }
}
public delegate void EditableListViewSubmitting(object sender, EditableListViewSubmittingEventArgs e);
public class EditableListView
{
public event EditableListViewSubmitting Submitting;
private ListView ListView { get; set; }
private Point MousePosition = new Point();
private TextBox EditBox { get; set; }
public int[] TextBoxColumns { get; set; }
public EditableListView(ListView listView)
{
// 初始化 EditBox
EditBox = new TextBox();
EditBox.Visible = false;
EditBox.KeyUp += new KeyEventHandler(delegate(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Escape)
{
LeaveEdit();
}
else if (e.KeyCode == Keys.Enter)
{
if (Submitting != null)
{
EditableListViewSubmittingEventArgs args = new EditableListViewSubmittingEventArgs();
if (EditBox.Tag != null)
{
args.Cell = (ListViewCell)EditBox.Tag;
}
else
{
args.Cell = null;
}
args.Value = EditBox.Text;
Submitting(listView, args);
}
}
});
// 设置 ListView
ListView = listView;
ListView.MouseMove += new MouseEventHandler(delegate(object sender, MouseEventArgs e)
{
// 记录鼠标位置,便于在鼠标动作中使用(有些鼠标动作,比如双击,事件中并不传递鼠标双击时的位置)。
MousePosition.X = e.X;
MousePosition.Y = e.Y;
});
EditBox.Parent = ListView;
// 事件
ListView.DoubleClick += new EventHandler(delegate(object sender, EventArgs e)
{
ListViewCell cell = ListViewCellLocator.GetCell(this.ListView, MousePosition.X, MousePosition.Y);
if (cell == null)
{
return;
}
if (TextBoxColumns.Contains(cell.Column.Index))
{
// 设置 EditBox 的位置、大小、内容、可显示等。
EditBox.Bounds = cell.Bounds;
EditBox.Text = cell.Item.SubItems[cell.Column.Index].Text;
EditBox.Visible = true;
EditBox.Focus();
EditBox.Tag = cell;
}
});
}
public bool IsEditableColumn(int columnIndex)
{
if (TextBoxColumns.Contains(columnIndex))
{
return true;
}
return false;
}
public void LeaveEdit()
{
EditBox.Visible = false;
EditBox.Tag = null;
}
}
}
<span style="font-family: SimSun, serif; font-size: 14px; line-height: 21px; text-indent: 28px;"><strong>应用</strong></span>
<span style="font-family: SimSun, serif; font-size: 14px; line-height: 21px; text-indent: 28px;"><strong>
</strong></span>
<span style="font-family: SimSun, serif; font-size: 14px; line-height: 21px; text-indent: 28px;"><strong>
</strong></span>
<span style="font-family: SimSun, serif; font-size: 14px; line-height: 21px; text-indent: 28px;"></span><pre name="code" class="csharp">EditablelistViewSubmittingEventArgs e)
{
if (e.Cell == null)
{
return;
}
int dataId = Convert.ToInt32(e.Cell.Item.SubItems[_dataIdColumn.Index].Text);
string newValue = e.Value;
// ... 更新代码省略
});