Updating Mulitple ListView Rows

In a previous post, I used the column header to sort the ListView. Now I’ve abandoned sorting and I’m using column headers for something else.

I need a way to easily add the same data to all of the ListItems. My ListItems are products and each product has an associated Board Software Package (BSP). The vast majority of the time, the BSP will be the same for every product on the order. There are situations where this is not true, but mostly it is. I need to add “v14.50? to each BSP column of the ListView to indicate the version that each product gets.

My original approach was to make the ListView multiselect. I envisioned that the user would select all the items and even added a “Select All” button to facilitate that. The textboxes used to edit the data normally would show the information for the selected item. But if more than one item is selected, I would have to do something different. My thought was that I would loop through all of the selected items and only display the information if it was the same for each item. If two items are selected and they both have “v14.50? in the BSP column, then I would show “v14.50? in the BSP textbox. If, however, one item has “v14.50? and the other has “v14.30?, I would show “Multiple” in the textbox.

I began coding that piece and immediately I didn’t like it. The ListView’s ItemCheck event passes an Item variable – only one Item, not an array of all the checked Items. That means the event will fire once for every Item that is checked and the event would loop through each Item in the ListView. It seemed very cumbersome, so I started looking for a different approach.

I feel like I’m trading ease of use for ease of coding. At some point in my coding career, I established (in my own mind) a correlation between how easy something is to code and how easy it is to use. I suspect I did this because I love nice, clean code. And probably because I’m lazy.

My final approach was to use the ColumnClick event. First, I changed the Multiselect property to False. This made coding the textboxes much easier.

Private Sub lsvProduction_ItemClick(ByVal Item As MSComctlLib.ListItem)
   
    Set mobjListItem2 = Item
    UnlockTextBoxes 2, Item.Selected
    FillTextBoxes 2, Item, Item.Selected
   
End Sub
 
Private Sub UnlockTextBoxes(ByVal lPage As Long, ByVal bSelected As Boolean)
   
    Select Case lPage
        Case 2
            Me.tbxBSP.Locked = Not bSelected
            Me.tbxMain.Locked = Not bSelected
            Me.tbxPBI1.Locked = Not bSelected
            Me.tbxPBI2.Locked = Not bSelected
            Me.tbxKeyCode.Locked = Not bSelected
    End Select
   
End Sub
 
Private Sub FillTextBoxes(ByVal lPage As Long, Item As MSComctlLib.ListItem, _
    ByVal bSelected As Boolean)
   
    Select Case lPage
        Case 2
            Me.tbxItem2.Text = IIf(bSelected, Item.Text, “”)
            Me.tbxDesc2.Text = IIf(bSelected, Item.SubItems(1), “”)
            Me.tbxBSP.Text = IIf(bSelected, Item.SubItems(2), “”)
            Me.tbxMain.Text = IIf(bSelected, Item.SubItems(3), “”)
            Me.tbxPBI1.Text = IIf(bSelected, Item.SubItems(4), “”)
            Me.tbxPBI2.Text = IIf(bSelected, Item.SubItems(5), “”)
            Me.tbxKeyCode.Text = IIf(bSelected, Item.SubItems(6), “”)
    End Select
   
End Sub

Don’t mind all the 2’s and Select Cases. I have ListViews on each page of a Multipage control and I want to use the same procedures to fill the textboxes. This approach allows me to hold the currently selected Item in a module-level variable which makes it much easier to write back to the ListView when something in a textbox changes.

Private Sub tbxBSP_Change()
   
    mobjListItem2.SubItems(2) = Me.tbxBSP.Text
   
End Sub

To handle multiple row updates, I use the ColumnClick event. The first two columns aren’t allowed to be changed, so I start my Select Case with column 3. The Select Case sets a Textbox variable and the loop writes that textbox’s Text property to each row.

Private Sub lsvProduction_ColumnClick(ByVal ColumnHeader As MSComctlLib.ColumnHeader)
   
    Dim i As Long
    Dim tbx As MSForms.TextBox
    Dim lSubItem As Long
   
    lSubItem = ColumnHeader.Position – 1
   
    Select Case ColumnHeader.Position
        Case 3
            Set tbx = Me.tbxBSP
        Case 4
            Set tbx = Me.tbxMain
        Case 5
            Set tbx = Me.tbxPBI1
        Case 6
            Set tbx = Me.tbxPBI2
        Case 7
            Set tbx = Me.tbxKeyCode
    End Select
   
    With Me.lsvProduction
        If Not tbx Is Nothing Then
            For i = 1 To .ListItems.Count
                .ListItems(i).SubItems(lSubItem) = tbx.Text
            Next i
        End If
    End With
   
End Sub

I’ve definitely sacrificed some usability here. The first thing I sacrificed is intuitiveness. While most people are familiar with multiselect, particularly when there are checkboxes next to each item, nobody without proper training is going to know to click on the column header. Not that the multiselect route was perfect. I had a hard time envisioning how that was going to work. For instance, I could see someone accidentally filling “Multiple” into every Item, which would be bad.

The other sacrifice my users unknowingly made is the ability to update multiple rows while leaving others unchanged. The ColumnClick approach updates all items, which is less usable than being able to update, say, half of them. Updating all items will be the proper course in about 90% of the cases, so I don’t feel too bad about it.

Then there’s the abandonment of the sort feature, which I won’t miss at all.

I made a few other changes to the ListView in the process. I set the LabelEdit property to lvwManual. I have no idea what lvwManual is supposed to represent, but it means the user can’t edit the Item inside the ListView. I was not able to set FullRowSelect to True and have it persist, so I had to set it in code in the Initialize procedure.

Posted in Uncategorized

One thought on “Updating Mulitple ListView Rows


Posting code? Use <pre> tags for VBA and <code> tags for inline.

Leave a Reply

Your email address will not be published.