DJDave DJDave - 9 months ago 57
Vb.net Question

programmatically select and highlight a row of a ListView in VB.NET

I want to do something seemingly simple - programmatically select and highlight a row of a ListView in VB.NET.

VB.NET: How to dynamically select a list view item?

tells me what should to be all that is needed, but it isn't. The row is selected, but not highlighted.

http://vbcity.com/forums/t/28260.aspx

tells me about the "HideSelection" property and the .Focus() method (also referenced at Select programmatically a row of a Listview), which sounded hopeful, but the best I can get is the faint highlight mentioned, I want the full monty. I tried a Noddy example with just a ListView, in Details mode, FullRowSelection = true, HideSelection = False, one columnheader defined and then

ListView1.Items.Add("Red")
ListView1.Items.Add("Orange")
ListView1.Items.Add("Yellow")
ListView1.Items.Add("Green")

ListView1.Items(2).Selected = True


I get this

enter image description here

but I want this

enter image description here

I can simulate highlighting by adding these lines

ListView1.SelectedItems(0).BackColor = Color.CornflowerBlue
ListView1.SelectedItems(0).ForeColor = Color.White


but then how can I be sure to undo the artificial highlight if the row can be implicitly as well as explicitly deselected? Do I have to think of all the possible cases? That's too much work for what should be a simple operation. Plus, since I want to color-code my rows, there is an additional challenge that when I undo the highlight color, I have to figure out what color is appropriate at that point. Is there a better, simpler way?

djv djv
Answer Source

You can access the Graphics object used to draw each item, and draw them yourself.

Make a new project with a Button and ListView. Paste the following code:

Form_Load to use multiple subitems

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    Me.ListView1.OwnerDraw = True ' or else can't handle DrawItem event
    ListView1.Columns.Add("ColumnHeader1")
    ListView1.Columns.Add("ColumnHeader2")
    ListView1.Columns.Add("ColumnHeader3")
    Me.ListView1.Items.Add("Red")
    Me.ListView1.Items.Add("Orange")
    Me.ListView1.Items.Add("Yellow")
    Me.ListView1.Items.Add("Green")
    ListView1.Items(0).SubItems.Add("Strawberry")
    ListView1.Items(0).SubItems.Add("Apple")
    ListView1.Items(1).SubItems.Add("Pepper")
    ListView1.Items(1).SubItems.Add("Apricot")
    ListView1.Items(2).SubItems.Add("Plum")
    ListView1.Items(2).SubItems.Add("Banana")
    ListView1.Items(3).SubItems.Add("Apple")
    ListView1.Items(3).SubItems.Add("Lime")
End Sub

Three handlers for the ListView's drawing related events. Code copied from this answer

Private Sub listView1_DrawColumnHeader(sender As Object, e As DrawListViewColumnHeaderEventArgs) Handles ListView1.DrawColumnHeader
    e.DrawDefault = True
End Sub

Private Sub listView1_DrawSubItem(sender As Object, e As DrawListViewSubItemEventArgs) Handles ListView1.DrawSubItem
    Const TEXT_OFFSET As Integer = 1
    ' I don't know why the text is located at 1px to the right. Maybe it's only for me.
    Dim listView As ListView = DirectCast(sender, ListView)

    ' Check if e.Item is selected and the ListView has a focus.
    If Not listView.Focused AndAlso e.Item.Selected Then
        Dim rowBounds As Rectangle = e.SubItem.Bounds
        Dim labelBounds As Rectangle = e.Item.GetBounds(ItemBoundsPortion.Label)
        Dim leftMargin As Integer = labelBounds.Left - TEXT_OFFSET
        Dim bounds As New Rectangle(rowBounds.Left + leftMargin, rowBounds.Top, If(e.ColumnIndex = 0, labelBounds.Width, (rowBounds.Width - leftMargin - TEXT_OFFSET)), rowBounds.Height)
        Dim align As TextFormatFlags
        Select Case listView.Columns(e.ColumnIndex).TextAlign
            Case HorizontalAlignment.Right
                align = TextFormatFlags.Right
                Exit Select
            Case HorizontalAlignment.Center
                align = TextFormatFlags.HorizontalCenter
                Exit Select
            Case Else
                align = TextFormatFlags.Left
                Exit Select
        End Select
        TextRenderer.DrawText(e.Graphics, e.SubItem.Text, listView.Font, bounds, SystemColors.HighlightText, align Or TextFormatFlags.SingleLine Or TextFormatFlags.GlyphOverhangPadding Or TextFormatFlags.VerticalCenter Or TextFormatFlags.WordEllipsis)
    Else
        e.DrawDefault = True
    End If
End Sub

Private Sub listView1_DrawItem(sender As Object, e As DrawListViewItemEventArgs) Handles ListView1.DrawItem
    Dim listView As ListView = DirectCast(sender, ListView)

    ' Check if e.Item is selected and the ListView has a focus.
    If Not listView.Focused AndAlso e.Item.Selected Then
        Dim rowBounds As Rectangle = e.Bounds
        Dim leftMargin As Integer = e.Item.GetBounds(ItemBoundsPortion.Label).Left
        Dim bounds As New Rectangle(leftMargin, rowBounds.Top, rowBounds.Width - leftMargin, rowBounds.Height)
        e.Graphics.FillRectangle(SystemBrushes.Highlight, bounds)
    Else
        e.DrawDefault = True
    End If
End Sub

Button click handler to simulate item(2) selected

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Me.ListView1.Items(2).Selected = True
End Sub

This will draw the background color regardless of focus. You have a lot of control over other colors and fonts going this route too.

Here, the button has been clicked, to select item 2, while the button still has focus, and item 2 is selected.

enter image description here

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download