Putting Checkboxes in any cell in listview

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • !NoItAll
    Contributor
    • May 2006
    • 297

    Putting Checkboxes in any cell in listview

    I needed to put checkboxes in several cells in a listview control. The way the listview control works you can really only have an "official" checkbox in the first cell, and attempts to put them anywhere else are thwarted at just about every turn.
    So I developed a simple approach using WingDings. The WingDings font has a checked and unchecked box. In this project I place the unchecked box from WingDings centered in any cell I want a checkbox in, and clicking on the cell will result in toggling between the WingDing checked and unchecked character.
    Chr(168) is checked and Chr(254) is unchecked.
    (float your mouse over the image below to see the whole thing....)

    The trick is to set the font in specific cells to WingDings.
    First we have to create a font object:
    Code:
    Dim myCheckFont As New System.Drawing.Font("Wingdings", 12, FontStyle.Regular)
    Then we need to assign it to the specific cell in which we want to have a checkbox. You set the font as you create the cells. So in the following example I am creating a table of 16 rows in a listview instantiated with 3 columns. Each time we add an item to column 1 we set the font to MyCheckFont (the WingDings font as assigned above).
    Code:
            With ListView1
                For I As Integer = 0 To 15
                    .Items.Add("test" & I.ToString)
                    .Items(I).UseItemStyleForSubItems = False
                    .Items(I).SubItems.Add(Chr(168))
                    .Items(I).SubItems.Item(1).ForeColor = Color.DarkRed
                    .Items(I).SubItems.Item(1).Font = myCheckFont
                    ReDim bOWCheck(I)
                Next I
            End With
    The trick here is to make sure we set UseItemSylteFor SubItems to FALSE. If we do not do that then we can not have separate fonts for different columns. That's a real nice piece of esoterica!
    Finaly the only thing you then need to manage is to catch the clicks in the particular column and row so you can then toggle between the two WingDing characters that represent the checked and unchecked boxes.
    This too is steeped in some esoteric functionality. Why MS didn't give us a simple way to get the cell that was clicked is beyond me - but they didn't so we have to do it the hard way.
    In the listview MouseUp event I have the following code
    Code:
        Private Sub ListView1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListView1.MouseUp
            Dim cellLoc As MyCell
            cellLoc = WhichCell(ListView1, e.X, e.Y)
            If cellLoc.Col = 2 Then
                If bOWCheck(cellLoc.Row) = True Then
                    ListView1.Items(cellLoc.Row).SubItems(cellLoc.Col - 1).Text = Chr(168)
                    bOWCheck(cellLoc.Row) = False
                Else
                    ListView1.Items(cellLoc.Row).SubItems(cellLoc.Col - 1).Text = Chr(254)
                    bOWCheck(cellLoc.Row) = True
                End If
    
            End If
    End Sub
    Notice the WhichCell function - that's doing the esoteric work...
    Code:
        Private Function WhichCell(ByVal lvw As ListView, ByVal X As Integer, ByVal Y As Integer) As MyCell
    
            Dim colstart As Integer = 0
            Dim colend As Integer = 0
            Dim xCol As Integer
    
            For xCol = 0 To (ListView1.Columns.Count - 1)
                colend = colend + ListView1.Columns(xCol).Width
                If colstart <= X And X <= colend Then
                    WhichCell.Col = xCol + 1
                    Exit For
                End If
                colstart = colstart + ListView1.Columns(xCol).Width
            Next
    
            WhichCell.Row = ListView1.FocusedItem.Index
            Return WhichCell
    
        End Function
    Finally - to keep track of the checkboxes I have a boolean array: bOWCheck() that I index to the current row. If you have multiple column just add another dimension to the array. This is where we persist the state of the checkbox for each cell. Notice it is toggled in the listview MouseUp event along with toggling the WingDing checked and unchecked characters.

    There are possibly several better ways to do this. Improvements are certainly welcome.
    I've attached a zip file with a VS2008 VB.NET project you can play with.
    Attached Files
  • raindrops
    New Member
    • Jan 2015
    • 1

    #2
    This is a really neat trick. Well done for working it out and sharing.

    Comment

    • BruceG1
      New Member
      • Dec 2012
      • 2

      #3
      Just came across this, and was exactly what I was looking for. Very clever and easy to implement. Thanks for posting this!

      Comment

      • IronRazer
        New Member
        • Jan 2013
        • 83

        #4
        Kind of handy if you really want to use a ListView but, it is much easier to just use a DataGridView which can be made to look just like a ListView and can easily have a checkbox column on any column(s) you want. 8)

        Comment

        • !NoItAll
          Contributor
          • May 2006
          • 297

          #5
          It's been a long time since I wrote this post. There was a reason I didn't use the dataGridView at the time. It might not have been a good reason - but it was a reason... ;-)

          Comment

          • BruceG1
            New Member
            • Dec 2012
            • 2

            #6
            Hey guys - I am aware that a DataGridView could be used, and in fact use that quite a bit. The reason this came in handy is that I had an existing ListView and needed to add checkbox columns to it "after the fact". Rather than rewriting that part of the interface from scratch with the DGV, it was quite handy to add these checkbox columns to the existing LV.

            Comment

            • amin grf
              New Member
              • May 2023
              • 2

              #7
              Thanks for sharing, I've added an option to select only one item at a time, in Mouse_Up event.
              Code:
               Private Sub ListView1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListView1.MouseUp
                      Dim cellLoc As MyCell
                      cellLoc = WhichCell(ListView1, e.X, e.Y)
              
                      If cellLoc.Col = 2 Then
                          'check once
                          ListView1.Items(check1_indx).SubItems(1).Text = Chr(168)
                          bOWCheck(cellLoc.Row) = False
                          If bOWCheck(cellLoc.Row) = False Then
                              ListView1.Items(cellLoc.Row).SubItems(cellLoc.Col - 1).Text = Chr(254)
                              bOWCheck(cellLoc.Row) = True
                              check1_indx = ListView1.Items(cellLoc.Row).Index
                              Label1.Text = "checked: " & check1_indx
                          End If
              
              
                          ''check multiple
                          'If bOWCheck(cellLoc.Row) = True Then
                          '    ListView1.Items(cellLoc.Row).SubItems(cellLoc.Col - 1).Text = Chr(168)
                          '    bOWCheck(cellLoc.Row) = False
                          'Else
                          '    'uncheck
                          '    ListView1.Items(cellLoc.Row).SubItems(cellLoc.Col - 1).Text = Chr(254)
                          '    bOWCheck(cellLoc.Row) = True
                          '    check1_indx = ListView1.Items(cellLoc.Row).Index
                          '    Label1.Text = "checked: " & check1_indx
                          'End If
              
                      End If
              
              
                      Label1.Focus()
                      ListView1.Select()
                  End Sub

              Comment

              • amin grf
                New Member
                • May 2023
                • 2

                #8
                Previous code when listview sort order change also change it's index, so some items will not be checked or unchecked. In this code I used Selected item. and added a list to store checked items.
                Code:
                    Dim checklist As New List(Of String)()
                    Private Sub lvwBooks_MouseUp(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles lvwBooks.MouseUp
                        Try
                            If Not lvwBooks.Items.Count = 0 Then
                                Dim cellLoc As MyCell
                                Dim check_chr As String = Chr(254)
                                Dim uncheck_chr As String = Chr(168)
                                Dim CheckdItems As String
                                Dim select_once As Boolean = True
                                cellLoc = WhichCell(lvwBooks, e.X, e.Y)
                                If cellLoc.Col = 1 Or cellLoc.Col = 2 Or cellLoc.Col = 3 Then
                                    If select_once = True Then
                                        'check_uncheck once
                                        lvwBooks.MultiSelect = False
                                        'uncheck 
                                        Dim unCheckItem As ListViewItem = lvwBooks.FindItemWithText(check_chr, True, 0, False)
                                        If Not unCheckItem Is Nothing Then
                                            unCheckItem.SubItems(2).Text = uncheck_chr
                                            checklist.Remove(unCheckItem.SubItems(1).Text)
                                        End If
                
                                        For Each item In lvwBooks.SelectedItems
                                            If item.SubItems(2).Text = check_chr Then
                                                'uncheck
                                                item.SubItems(2).Text = uncheck_chr
                                                checklist.Remove(item.SubItems(1).Text)
                                            Else
                                                'check
                                                item.SubItems(2).Text = check_chr
                                                checklist.Add(item.SubItems(1).Text)
                                            End If
                
                                            Exit For
                                        Next
                                    Else
                                        'check_uncheck multiple
                                        lvwBooks.MultiSelect = True
                                        For Each item In lvwBooks.SelectedItems
                                            If item.SubItems(2).Text = check_chr Then
                                                item.SubItems(2).Text = uncheck_chr
                                                checklist.Remove(item.SubItems(1).Text)
                                            Else
                                                item.SubItems(2).Text = check_chr
                                                checklist.Add(item.SubItems(1).Text)
                                            End If
                                        Next
                
                                    End If
                
                                End If
                                CheckdItems = ""
                                For Each c In checklist
                                    CheckdItems += c & " "
                                Next
                                If checklist.Count = 0 Then
                                    Label1.Text = "No Item Checked"
                                Else
                                    Label1.Text = "Checked: " & CheckdItems
                                End If
                
                                'lvwBooks.Select()
                            End If
                        Catch ex As Exception
                           
                             MsgBox(ex.Message)
                         
                        End Try
                    End Sub

                Comment

                Working...