SUMMARY
This step-by-step article demonstrates how to use a
ComboBox control to edit the data in a
ListView control. This method replaces the standard text box approach of editing the data in a
ListView control.
Description of the technique
By using the
LabelEdit property of the
ListView control, you can let the user edit the contents of the
ListView control. To edit the data in the
ListView control, you can use a standard text box. Occasionally, it is useful to have another control to edit the control. This article simulates how to use a
ComboBox control to edit the data in a
ListView when the
ListView is in Details view.
When the user selects a row in the
ListView, a calculation is performed to locate the bounding rectangle for the first column of the row that is clicked. That calculation takes into account that the column may not be visible or may not be fully visible when the row is clicked and when the
ComboBox is sized and displayed appropriately.
In addition to positioning and sizing the
ComboBox, this sample also watches for two messages on the
ListView control: WM_VSCROLL and WM_HSCROLL. These messages occur whenever the user scrolls through the
ListView control vertically or horizontally. Because the
ComboBox is not physically part of the
ListView control, the
ComboBox does not automatically scroll with the
ListView. Therefore, whenever either of these two messages occur, the
ComboBox must be hidden. To watch for these messages, you create a custom
UserControl class that inherits from the
ListView class. In this custom control, the
WndProc method is overridden to allow all messages to be checked for scrolling.
Create the inherited ListView control
- Start Microsoft Visual Studio 2005 or Microsoft Visual Studio .NET.
- On the File menu, point to New, and then click Project.
- In the New Project dialog box, click Visual Basic Projects under Project Types, and then click Windows Control Library under Templates.
Note In Visual Studio 2005, click Visual Basic instead of Visual Basic Projects. - Replace all the code in the UserControl class with the following code.
Imports System
Imports System.Collections
Imports System.ComponentModel
Imports System.Drawing
Imports System.Data
Imports System.Windows.Forms
Public Class MyListView
Inherits System.Windows.Forms.ListView
#Region " Windows Form Designer generated code "
Public Sub New()
MyBase.New()
'This call is required by the Windows Form Designer.
InitializeComponent()
'Add any initialization after the InitializeComponent() call
End Sub
'UserControl1 overrides dispose to clean up the component list.
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
'Required by the Windows Form Designer
Private components As System.ComponentModel.IContainer
'NOTE: The following procedure is required by the Windows Form Designer
'It can be modified using the Windows Form Designer.
'Do not modify it using the code editor.
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
components = New System.ComponentModel.Container()
End Sub
#End Region
Private Const WM_HSCROLL As Integer = &H114
Private Const WM_VSCROLL As Integer = &H115
Protected Overrides Sub WndProc(ByRef msg As Message)
' Look for the WM_VSCROLL or the WM_HSCROLL messages.
If ((msg.Msg = WM_VSCROLL) Or (msg.Msg = WM_HSCROLL)) Then
' Move focus to the ListView to cause ComboBox to lose focus.
Me.Focus()
End If
' Pass message to default handler.
MyBase.WndProc(msg)
End Sub
End Class
- Save and then build the project.
Create the sample application
- Follow these steps to create a Windows Application in Visual Basic 2005 or in Visual Basic .NET:
- On the File menu, point to New, and then click Project.
- In the New Project dialog box, click Visual Basic Projects under Project Types, and then click Windows Application under Templates. By default, Form1 is created.
Note In Visual Studio 2005, click Visual Basic instead of Visual Basic Projects.
Note
You must change the code in Visual Basic 2005. By default, Visual Basic creates two files for the project when you create a Windows Forms project. If the form is named Form1, the two files that represent the form are named Form1.vb and Form1.Designer.vb. You write the code in the Form1.vb file. The Windows Forms Designer writes the code in the Form1.Designer.vb file. The Windows Forms Designer uses the partial keyword to divide the implementation of Form1 into two separate files. This behavior prevents the designer-generated code from being interspersed with your code.
For more information about the new Visual Basic 2005 language enhancements, visit the following Microsoft Developer Network (MSDN) Web site: For more information about partial classes and the Windows Forms Designer, visit the following MSDN Web site:
- Follow these steps to add the control that you created in the "Create the inherited ListView control" section to your Windows application:
- On the Tools menu, click Customize Toolbox.
- On the .NET Framework Components tab, click Browse.
- In the Open dialog box, locate the control that you created in the "Create the inherited ListView control" section, and then click Open. This adds this control to the toolbox so that you can use the control similarly to any other control.
- Drag MyListView from the General section of the toolbox to Form1.
- Drag a ComboBox control from the Windows Forms section of the toolbox to Form1.
- In the Properties window of ComboBox, change the Name property to cbListViewCombo, and then set the Visible property to False.
- Add the following code to the class of Form1 before the "Windows Form Designer generated code" section:
Private lvItem As ListViewItem
- Add the following code to the Load event of Form1.
' Add a few items to the combo box list.
Me.cbListViewCombo.Items.Add("NC")
Me.cbListViewCombo.Items.Add("WA")
' Set view of ListView to Details.
Me.MyListView1.View = View.Details
' Turn on full row select.
Me.MyListView1.FullRowSelect = True
' Add some data to the ListView.
Dim columnheader As ColumnHeader
Dim lviewitem As ListViewItem
' Create sample ListView data.
lviewitem = New ListViewItem("NC")
lviewitem.SubItems.Add("North Carolina")
Me.MyListView1.Items.Add(lviewitem)
lviewitem = New ListViewItem("WA")
lviewitem.SubItems.Add("Washington")
Me.MyListView1.Items.Add(lviewitem)
' Create column headers for the data.
columnheader = New ColumnHeader()
columnheader.Text = "State Abbr."
Me.MyListView1.Columns.Add(columnheader)
columnheader = New ColumnHeader()
columnheader.Text = "State"
Me.MyListView1.Columns.Add(columnheader)
' Loop through and size each column header to fit the column header text.
Dim ch As ColumnHeader
For Each ch In Me.MyListView1.Columns
ch.Width = -2
Next
- Add the following code to the SelectedValueChanged event of the ComboBox control.
' Set text of ListView item to match the ComboBox.
lvItem.Text = Me.cbListViewCombo.Text
' Hide the ComboBox.
Me.cbListViewCombo.Visible = False
- Add the following code to the Leave event of the ComboBox control.
' Set text of ListView item to match the ComboBox.
lvItem.Text = Me.cbListViewCombo.Text
' Hide the ComboBox.
Me.cbListViewCombo.Visible = False
- Add the following code to the KeyPress event of the ComboBox control.
' Verify that the user presses ESC.
Select Case (e.KeyChar)
Case ChrW(CType(Keys.Escape, Integer))
' Reset the original text value, and then hide the ComboBox.
Me.cbListViewCombo.Text = lvItem.Text
Me.cbListViewCombo.Visible = False
Case ChrW(CType(Keys.Enter, Integer))
' Hide the ComboBox.
Me.cbListViewCombo.Visible = False
End Select
- Add the following code to the MouseUp event of myListView1.
' Get the item on the row that is clicked.
lvItem = Me.MyListView1.GetItemAt(e.X, e.Y)
' Make sure that an item is clicked.
If Not (lvItem Is Nothing) Then
' Get the bounds of the item that is clicked.
Dim ClickedItem As Rectangle = lvItem.Bounds
' Verify that the column is completely scrolled off to the left.
If ((ClickedItem.Left + Me.MyListView1.Columns(0).Width) < 0) Then
' If the cell is out of view to the left, do nothing.
Return
' Verify that the column is partially scrolled off to the left.
ElseIf (ClickedItem.Left < 0) Then
' Determine if column extends beyond right side of ListView.
If ((ClickedItem.Left + Me.MyListView1.Columns(0).Width) > Me.MyListView1.Width) Then
' Set width of column to match width of ListView.
ClickedItem.Width = Me.MyListView1.Width
ClickedItem.X = 0
Else
' Right side of cell is in view.
ClickedItem.Width = Me.MyListView1.Columns(0).Width + ClickedItem.Left
ClickedItem.X = 2
End If
ElseIf (Me.MyListView1.Columns(0).Width > Me.MyListView1.Width) Then
ClickedItem.Width = Me.MyListView1.Width
Else
ClickedItem.Width = Me.MyListView1.Columns(0).Width
ClickedItem.X = 2
End If
' Adjust the top to account for the location of the ListView.
ClickedItem.Y += Me.MyListView1.Top
ClickedItem.X += Me.MyListView1.Left
' Assign calculated bounds to the ComboBox.
Me.cbListViewCombo.Bounds = ClickedItem
' Set default text for ComboBox to match the item that is clicked.
Me.cbListViewCombo.Text = lvItem.Text
' Display the ComboBox, and make sure that it is on top with focus.
Me.cbListViewCombo.Visible = True
Me.cbListViewCombo.BringToFront()
Me.cbListViewCombo.Focus()
End If
Verify that it works
- Save and then run the sample.
- Click a row in the ListView control. Notice that a combo box appears over the location of the first column of the current row.
- To hide the combo box, click an item in the combo box, press ESC, and then scroll through the ListView control or click another control. Notice that the value that you clicked in the combo box is placed in the first column of the clicked row of the ListView control.