Access ListView SubItem Text by Column Header


I have a ListView that I would like to access SubItem text from the selected row.  The ListView is dynamically populated so I am not always sure what SubItem relates to the info I need.  When populating the listview I included column headers but i am not sure how I can reference a selected listview item subItem by column header.

I can get the value with the following line, however i cant guarantee subitems(5) will be the data i need to get.

val = lvwData.Items(SelectedItemIndex).SubItems(5).Text

If I cant do this direct by column header name, is there some way i can determine the SubItems index by the known column name

Thank you for any advise
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Hi QuinnB74,

I do not know if this will help you ,but here is a sample that you might be able to modify...


Public Class frmClickSubItem
    Inherits System.Windows.Forms.Form

#Region " Windows Form Designer generated code "

    Public Sub New()

        'This call is required by the Windows Form Designer.

        'Add any initialization after the InitializeComponent() call

    End Sub

    'Form 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
            End If
        End If
    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.
    Friend WithEvents ListView1 As System.Windows.Forms.ListView
    Friend WithEvents Label1 As System.Windows.Forms.Label
    Friend WithEvents ColumnHeader1 As System.Windows.Forms.ColumnHeader
    Friend WithEvents ColumnHeader2 As System.Windows.Forms.ColumnHeader
    Friend WithEvents ColumnHeader3 As System.Windows.Forms.ColumnHeader
    Friend WithEvents ColumnHeader4 As System.Windows.Forms.ColumnHeader
    Friend WithEvents ColumnHeader5 As System.Windows.Forms.ColumnHeader
    Friend WithEvents TextBox1 As System.Windows.Forms.TextBox
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        Me.ListView1 = New System.Windows.Forms.ListView
        Me.ColumnHeader1 = New System.Windows.Forms.ColumnHeader
        Me.ColumnHeader2 = New System.Windows.Forms.ColumnHeader
        Me.ColumnHeader3 = New System.Windows.Forms.ColumnHeader
        Me.ColumnHeader4 = New System.Windows.Forms.ColumnHeader
        Me.ColumnHeader5 = New System.Windows.Forms.ColumnHeader
        Me.Label1 = New System.Windows.Forms.Label
        Me.TextBox1 = New System.Windows.Forms.TextBox
        Me.ListView1.Anchor = CType((((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Bottom) _
                    Or System.Windows.Forms.AnchorStyles.Left) _
                    Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
        Me.ListView1.Columns.AddRange(New System.Windows.Forms.ColumnHeader() {Me.ColumnHeader1, Me.ColumnHeader2, Me.ColumnHeader3, Me.ColumnHeader4, Me.ColumnHeader5})
        Me.ListView1.FullRowSelect = True
        Me.ListView1.GridLines = True
        Me.ListView1.Location = New System.Drawing.Point(8, 8)
        Me.ListView1.MultiSelect = False
        Me.ListView1.Name = "ListView1"
        Me.ListView1.Size = New System.Drawing.Size(458, 206)
        Me.ListView1.TabIndex = 0
        Me.ListView1.View = System.Windows.Forms.View.Details
        Me.Label1.Anchor = CType(((System.Windows.Forms.AnchorStyles.Bottom Or System.Windows.Forms.AnchorStyles.Left) _
                    Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
        Me.Label1.Location = New System.Drawing.Point(8, 222)
        Me.Label1.Name = "Label1"
        Me.Label1.Size = New System.Drawing.Size(190, 16)
        Me.Label1.TabIndex = 1
        Me.Label1.Text = "Label1"
        Me.TextBox1.Location = New System.Drawing.Point(212, 220)
        Me.TextBox1.Name = "TextBox1"
        Me.TextBox1.TabIndex = 2
        Me.TextBox1.Text = "TextBox1"
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
        Me.ClientSize = New System.Drawing.Size(478, 244)
        Me.Name = "frmClickSubItem"
        Me.Text = "Form1"

    End Sub

#End Region

    Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As IntPtr, ByVal lMsg As Integer, ByVal wParam As Integer, ByRef lParam As RECT) As Integer

    Private Structure RECT
        Public Left As Integer
        Public Top As Integer
        Public Right As Integer
        Public Bottom As Integer
    End Structure
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim i As Integer
        Dim lvi As ListViewItem
        Dim ch As ColumnHeader

        For i = 0 To 10
            lvi = New ListViewItem("Item" & i)
            lvi.SubItems.Add("Subitems" & i * 1)
            lvi.SubItems.Add("Subitems" & i * 2)
            lvi.SubItems.Add("Subitems" & i * 3)
            lvi.SubItems.Add("Subitems" & i * 4)

        For Each ch In ListView1.Columns
            ch.Width = -2
    End Sub
    Private Sub ListView1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListView1.MouseDown
        Label1.Text = "Column " & (GetListViewSubItem(Me.ListView1, New Point(e.X, e.Y)) + 1)
    End Sub

    Public Function GetListViewSubItem(ByVal listView1 As ListView, ByVal pt As Point) As Integer
        Const LVM_FIRST As Integer = &H1000
        Const LVM_GETSUBITEMRECT As Integer = LVM_FIRST + 56
        Const LVIR_BOUNDS As Integer = 0

        Dim myrect As RECT
        Dim lvitem As ListViewItem = listView1.GetItemAt(pt.X, pt.Y)
        If lvitem Is Nothing AndAlso listView1.SelectedItems.Count > 0 Then
            lvitem = listView1.SelectedItems(0)
        End If
        Dim intLVSubItemIndex As Integer = -1
        Dim LVSubItem As ListViewItem.ListViewSubItem = Nothing

        If Not (lvitem Is Nothing) Then
            Dim intSendMessage As Integer
            Dim i As Integer
            lvitem.UseItemStyleForSubItems = False
            For i = 1 To lvitem.SubItems.Count - 1
                LVSubItem = lvitem.SubItems(i)
                myrect = New RECT
                myrect.Top = i
                myrect.Left = LVIR_BOUNDS
                intSendMessage = SendMessage(listView1.Handle, LVM_GETSUBITEMRECT, lvitem.Index, myrect)
                If pt.X < myrect.Left Then
                    LVSubItem = lvitem.SubItems(0)
                    LVSubItem.ForeColor = Color.Red
                    intLVSubItemIndex = 0
                    TextBox1.Text = LVSubItem.Text
                    Exit For
                ElseIf pt.X >= myrect.Left And pt.X <= myrect.Right Then
                    intLVSubItemIndex = i
                    LVSubItem.ForeColor = Color.Orange
                    TextBox1.Text = LVSubItem.Text
                    Exit For
                    LVSubItem = Nothing
                End If
            Next i
        End If
        If LVSubItem Is Nothing OrElse lvitem Is Nothing Then
            intLVSubItemIndex = -1
        End If
        Return intLVSubItemIndex
    End Function

End Class
QuinnB74Author Commented:
Thanks for the suggestion however the SubItem I am looking for is actually hidden from view (colWidth = 0) so I dont think your solution will apply to my situation. Sorry, I should have been clearer earlier.

Without getting too into it, I have inherited some code whereby the previous developer had a listview to display a dataTable.  This dev implemented column sorting on the listview, however any selection of the listview for action (ie delete) uses the listview selected index and passes that to the function which does the delete.  the Delete function uses the listview index to lookup the record ID in the backend Datatable and then performs the action.  Works great if the listview is unsorted as the indexes will line up, unfortunately ppl want the sort feature.  

I thought the easiest fix was to query the listview for the ID (btw, this is not always limited to one ID, it references multiple foreign keys that i need to perform action on depending on user input) which is added during population with a column width of 0.

Thank you

Bob LearnedCommented:
Try something like this:

  Public Function GetListViewSubItem(ByVal view As ListView, ByVal index As Integer, ByVal columnName As String) As ListViewItem.ListViewSubItem

    Dim item As ListViewItem = view.Items(index)

    For columnIndex As Integer = 0 To view.Columns.Count - 1
      Dim column As ColumnHeader = view.Columns(columnIndex)
      If column.Text.ToLower() = columnName.ToLower() Then
        Return item.SubItems(columnIndex)
      End If

    Next columnIndex

    Return Nothing

  End Function


Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
QuinnB74Author Commented:
Thank You Kindly. It seems so logical now.
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Visual Basic.NET

From novice to tech pro — start learning today.