http://www.codeproject.com/cs/miscctrl/ListViewCellEditors.asp
In-place editing of ListView subitems
Download source files (including demo application) - 31.2 Kb
Introduction
While talking with one of our customers, he wanted an additional feature in one of our programs to let him drop a number of files onto our application and then modify certain properties of the resulting documents in a list.
Showing these documents in a ListView
can be done easily, but editing of single properties requires a little work (since built-in ListView
only allows plain editing of a ListViewItem
's text). Because I didn't find anything pre-built, I decided to write my own in-place editing for ListView
s, so here it is...
How it is done
In fact, in-place editing in a ListView
isn't too much magic, but there are a few places where the plain .NET framework classes aren't sufficient, so I had to use a little Interop.
仅仅靠一般的.net框架类是不行的,必须进行一些内部操作
First, you have to have a control to perform the actual editing of the SubItem
. Which control you use is (almost) completely up to you. TextBox
, ComboBox
or DateTimePicker
works fine, for example. Since this control is used only when a SubItem
has been clicked, it should be invisible in the beginning.
首先,我们必须有一个来对subitem进行实际编辑的控件,我们常用的有TextBox, ComboBox
or DateTimePicker
.
这些控件开始是不可见的,只有当SubItem 被点击后才可以使用.
Then you have to find out which SubItem
has been clicked. This part is quite straightforward, I've added a method GetSubItemAt()
to my ListViewEx
to make things a little easier.
你必须找到是那一个subitem被点击了,这部分相当易懂,我已经在我的ListViewEx里添加了GetSubItemAt()来简化操作
A little twist comes from column reordering. Standard ListView
allows you to rearrange its columns while in report view (AllowColumnReorder
property). Unfortunately, there is no built-in way to find out the current order of your columns, so this is where Interop came in handy:
还有一个麻烦来自列重新排序,标准的ListView控件允许你在report view的时候重新排序.不幸的是,没有内置的方法发现当前规则,所以必须有一个内部操作.
[DllImport("user32.dll", CharSet=CharSet.Ansi)]
private static extern IntPtr SendMessage(IntPtr hWnd,
int msg, int len, ref int [] order);
// ListView messages
private const int LVM_FIRST = 0x1000;
private const int LVM_GETCOLUMNORDERARRAY = (LVM_FIRST + 59);
Using these declarations, you can use the LVM_GETCOLUMNORDERARRAY
message to get the ListView
's current column order.
通过使用上面的声明,你可以使用LVM_GETCOLUMNORDERARRAY 来得到listview当前列排序的规则.
The next step is to move the editor control in place and to make it visible. Once the actual editing is being performed, the user must be able to accept or reject any changes he makes, so there are a few events that have to be caught while editing.
下一步操作是移动编辑控件到适当的位置,并且使它可见.一旦执行实际的编辑操作,用户必须马上接受或拒绝他做的任何操作,所以在编辑的时候,这里必须捕捉一些事件.
Usually, a click outside the editor control accepts any changes made, as does the Return key. Pressing ESC while in in-place editing mode converts back to the original SubItem
text.
通常当点击在编辑控件外面表示接受操作,按下回车按钮也是.在控件内部按下esc按钮表示回到原来的文本.
Because the editor control actually is not part of the ListView
, I also had to look for any action that might change the size or location of the editor control. This was done overriding WndProc
:
因为编辑控件不是listview的一部分,我必须寻找任何改变编辑控件尺寸和位置的动作.通过覆盖WndProc实现:
protected override void WndProc(ref Message msg)
{
switch (msg.Msg)
{
// Look for WM_VSCROLL, WM_HSCROLL or WM_SIZE messages.
case WM_VSCROLL:
case WM_HSCROLL:
case WM_SIZE:
EndEditing(false);
break;
case WM_NOTIFY:
// Look for WM_NOTIFY of events that might also change the
// editor's position/size: Column reordering or resizing
NMHDR h = (NMHDR)Marshal.PtrToStructure(msg.LParam, typeof(NMHDR));
if (h.code == HDN_BEGINDRAG ||
h.code == HDN_ITEMCHANGINGA ||
h.code == HDN_ITEMCHANGINGW)
EndEditing(false);
break;
}
base.WndProc(ref msg);
}
Here, scrolling and resizing of the ListView
are monitored as well as changes to the ListView
's column headers. If one of these messages is received, the input focus is transferred back to the ListView
, thus ending in-place editing.
如果受到其中一个消息,输入焦点被传回listview,结束编辑.
How to use ListViewEx for in-place editing
There are two ways to perform in-place editing with ListViewEx
. First, you can use the new SubItemClicked
event together with GetSubItemBounds()
to position your editor control by yourself, or you can use StartEditing()
, which performs all required calculations and control positioning by itself.
你可以使用新的SubItemClicked和GetSubItemBounds()来自己定位新的编辑控件.也可以使用StartEditing(),这个本身来执行所有的计算和控件定位.
So, usually you would start by adding a ListViewEx
and at least one control used as a cell editor to your Form
. Don't forget to make your cell editor control invisible! Then wire up an event handler for SubItemClicked
and actually start editing:
通常你应该先添加一个ListViewEx,并且至少使用一个控件作为一个cell editor,不要忘了使你的cell editor不可见.然后为SubItemClicked绑定一个事件句柄.
private void listViewEx1_SubItemClicked(object sender,
ListViewEx.SubItemClickEventArgs e)
{
// Here, I use a ComboBox (comboBox1) as a cell editor:
listViewEx1.StartEditing(comboBox1, e.Item, e.SubItem);
}
That's it.
I've included a small sample application to show you how to use ListViewEx
with several different cell editors. Feel free to use the control or the source to your heart's desire and have fun!
Additional features
Your comments gave me some hints on missing features, so meanwhile I've added an additional property DoubleClickActivation
so that you can decide if the ListViewEx should enter editing mode when you click on a subitem or if a double click is required.
Another point was adding two new events (SubItemBeginEditing
and SubItemEndEditing
) to give the caller the possibility to control what's displayed in the editing control and what gets put back into the ListViewSubItem
.
Now you're able to add a password field as a cell editor and transfer the plain password to and from the edit control without having it shown in the listview. Take a look at the sample project to see how it's done.
翻译太烦了,还是看源代码吧