ListView in-line editing
In Windows programming,
ListView is a great control! Especially with the variety of views it supports. But alas, the original
ListView control supplied
by Visual Studio is very limited and its endless possibilities cannot be exploited without going through core level programming. Editing the text in the columns
ListView control is one of the difficult tasks that one has to do to allow such basic facility to end-users.
The basic need of many programmers of
ListView control is to allow users to edit the content of the control during runtime by end-users.
ListView however only allows the first column to be edited in the ‘Detailed’ view. What about other columns?
There are many off-the-shelf free controls available on the internet that allow great facilities and bend the limits of the
ListView control. One noteworthy control
But somehow I feel why use such a difficult control, which is difficult to program as well, just to allow such a simple feature in the
So here’s a simple coding technique that allows your end-users to edit columns of the
ListView control without using any third-party controls.
All it does is monitor the keyboard and mouse clicks and uses a simple
TextBox control to allow editing.
Although my focus in this article is to enable in-line editing on the
ListView control, my sample project also shows basic XML document handling procedures,
like how to load, query, and save data in XML.
Using the Code
To begin making such an application, create a new Windows Forms Application in Visual Studio. We’re going to need the original
ListView control and a
Place these two controls on your form and set the
TextBox‘s visibility to
In my example, I will be looking at the user’s interaction with the
ListView control by checking the mouse clicks and keyboard keys.
A user can double-click on a
SubItem to begin editing, or can also press a shortcut key like F2 on keyboard to begin editing.
Private Sub ListView1_MouseDoubleClick(sender As Object, _ e As System.Windows.Forms.MouseEventArgs) Handles ListView1.MouseDoubleClick ' This subroutine checks where the double-clicking was performed and ' initiates in-line editing if user double-clicked on the right subitem ' check where clicked CurrentItem = ListView1.GetItemAt(e.X, e.Y) ' which listviewitem was clicked If CurrentItem Is Nothing Then Exit Sub CurrentSB = CurrentItem.GetSubItemAt(e.X, e.Y) ' which subitem was clicked ' See which column has been clicked ' NOTE: This portion is important. Here you can define your own ' rules as to which column can be edited and which cannot. Dim iSubIndex As Integer = CurrentItem.SubItems.IndexOf(CurrentSB) Select Case iSubIndex Case 2, 3 ' These two columns are allowed to be edited. So continue the code Case Else ' In my example I have defined that only "Runs" ' and "Wickets" columns can be edited by user Exit Sub End Select Dim lLeft = CurrentSB.Bounds.Left + 2 Dim lWidth As Integer = CurrentSB.Bounds.Width With TextBox1 .SetBounds(lLeft + ListView1.Left, CurrentSB.Bounds.Top + _ ListView1.Top, lWidth, CurrentSB.Bounds.Height) .Text = CurrentSB.Text .Show() .Focus() End With End Sub
MouseDoubleClick event, we have the flexibility to look at which
ListViewItem was clicked, and from there we can detect which
SubItem was clicked.
Here you have the opportunity to decide which column you want to make ‘read-only’. For example, you may not want some columns to be edited by users. In my example, I’ve only allowed
column number 2 and 3 to be edited during runtime. Rest of the columns are to be ‘read-only’. (See the Select-Case coding, it determines which columns to allow editing.)
Finally, we display a
TextBox control (which is already present on the form, but set as invisible) on top of the
SubItem which was double-clicked.
We resize the
TextBox to cover the entire area of that
The above code is triggered when the user double-clicks on the
SubItem. Now we also need to listen to keyboard strokes. The following code allows us to achieve this:
Private Sub LV_KeyDown(sender As Object, _ e As System.Windows.Forms.KeyEventArgs) Handles ListView1.KeyDown ' This subroutine is for starting editing when keyboard shortcut is pressed (e.g. F2 key) If ListView1.SelectedItems.Count = 0 Then Exit Sub Select Case e.KeyCode Case Keys.F2 ' F2 key is pressed. Initiate editing e.Handled = True BeginEditListItem(ListView1.SelectedItems(0), 2) End Select End Sub
As you can see in the above code, we’re listening for the F2 key in
KeyDown event. Once the F2 key is pressed, we have to manually initiate
SubItem editing. By manually I mean performing all that code again which was performed in
MouseDoubleClick. Since it’s not my programming style not
to repeat the same piece of code over and over again, I am going to fake a mouse double-click through the
KeyDown event. You don’t need to do this really.
You could come up with a better and more efficient piece of coding. A good programmer always minimizes his code by making it reusable as much as possible, and not write
the same code over and over again. Anyway, back to the topic…
BeginEditListItem is a subroutine that fakes a mouse double-click in order to trigger the
and commence editing a
Private Sub BeginEditListItem(iTm As ListViewItem, SubItemIndex As Integer) ' This subroutine is for manually initiating editing instead of mouse double-clicks Dim pt As Point = iTm.SubItems(SubItemIndex).Bounds.Location Dim ee As New System.Windows.Forms.MouseEventArgs(Windows.Forms.MouseButtons.Left, 2, pt.X, pt.Y, 0) Call ListView1_MouseDoubleClick(ListView1, ee) End Sub
In the above code, we fake a mouse double-click to initiate editing of a
Sub is called from the
KeyDown event, i.e., when F2 is pressed.
So.. at this point, we’re done with coding for initiating the editing of a
SubItem of a
ListItem in a
ListView control. We’ve captured
the mouse-double click and the keyboard’s F2 keystroke. Now we have to figure out when the user has finished editing the
To detect when the user has done editing a
SubItem, the following code is used:
Private Sub TextBox1_KeyPress(sender As Object, _ e As System.Windows.Forms.KeyPressEventArgs) Handles TextBox1.KeyPress ' This subroutine closes the text box Select Case e.KeyChar Case Microsoft.VisualBasic.ChrW(Keys.Return) ' Enter key is pressed bCancelEdit = False ' editing completed e.Handled = True TextBox1.Hide() Case Microsoft.VisualBasic.ChrW(Keys.Escape) ' Escape key is pressed bCancelEdit = True ' editing was cancelled e.Handled = True TextBox1.Hide() End Select End Sub
The above code monitors two types of keystrokes in the
TextBox, the Enter key and the Escape key. The Enter key means the editing is finished,
and the Escape key of course means cancel the editing. Whether to save the changes or not, it is saved in the
bCancelEdit variable and then the
TextBox is set to invisible again. By hiding the
TextBox, it triggers a
LostFocus event on the
From there, we will capture what new text was entered by the user and whether to update the
SubItem or discard the changes.
Private Sub TextBox1_LostFocus(sender As Object, e As System.EventArgs) Handles TextBox1.LostFocus TextBox1.Hide() If bCancelEdit = False Then ' update listviewitem CurrentSB.Text = CInt(TextBox1.Text).ToString("#,###0") Else ' Editing was cancelled by user bCancelEdit = False End If ListView1.Focus() End Sub
Here you can also set up validations on the text entered. For example, you might want only numeric values to be entered in that column, so you can check the
TextBox and show appropriate messages to the user before accepting the newly entered text.
That’s just about it. That’s all we need to do to enable editing on
SubItems. However, there’s a slight obstacle that we have to overcome…
ListView by default allows editing of the first column only. We did all the above code to make it work on its
SubItems as well. At the moment,
MouseDoubleClick event, we’ve only coded for detecting mouse double-clicks on
SubItems. What if the user had clicked on the first column?
It is really no issue except that you cannot get the
Bounds of the first column like you can get for
SubItems. If you try to check the
of the first column, it will return you the size of the entire row of the
ListItem and not just the first column. Whereas if you check the
Bounds property of any
SubItem other than the first column, it’ll tell you only the size of that particular column.
So if we show our
TextBox on the first column and resize it using the first column’s
Bounds property, it ends up spreading all over the entire row and covers
up all the
SubItems. So how do we overcome this obstacle? Easy..! We use the
ListView‘s originally supported label editing. All we have to do is to check
if the user double-clicked on the first column, and if that’s the case, we abort all our tricky coding and initiate the
ListView‘s own label editing by calling
BeginEdit method. For this, we have to add another piece of code to the
' Check if the first subitem is being edited If iSubIndex = 0 Then ' There's a slight coding difficulty here. If the first item is to be edited ' then when you get the Bounds of the first subitem, it returns the Bounds of ' the entire ListViewItem. Hence the Textbox looks very wierd. In order to allow ' editing on the first column, we use the built-in editing method. CurrentItem.BeginEdit() ' make sure the LabelEdit is set to True Exit Sub End If
This code is inserted right before we resize our
TextBox control. As you can see, we check whether the index of the
SubItem clicked was zero, i.e., the first column.
If so, we abort the code and call
BeginEdit on the selected
ListItem. Of course, this requires that
is set to
True prior to running into this code. Preferably at design-time.
Download and see the sample project. Please note, this project has been made with .NET Framework 2.0 using Visual Studio 10. You can switch to another framework
in case this framework is not installed on your system. (Open project properties, go to Compile tab on the left, click Advanced Compile Options, and select your target framework.)
Hope it helps improve your interface designing skills 🙂