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:
- 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).
-
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 UseItemSylteForSubItems 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
-
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...
-
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.