I have a gridview with a gridviewcheckboxcolumn and an enabled headercheckbox as well. I have Excel type filters enabled along with grouping. What I want to do is get a running count of how many items are checked (say out of 1,000).
Currently I am using a loop to get the checked cells by iterating through the RadGrid.MasterTemplate.DataView.ItemCount and checking each row checkbox value. This works ok, but where I have a problem is with the header check/uncheck all button. When I check the header button it will trigger the CellValueChanged event every time a cell changes. For 1,000 records this gets pretty slow.
So my questions are;
- Is there a CheckedCount method instead of iterating? would have to work on Excel filtered grid
- Is there a way to 'wait' for the header toggle to finish so that the CellValueChanged could be set not to trigger until done?
- Is there a simpler, better way to get a count of Checked rows in a grid?
Private
Sub
grdSales_CellValueChanged(sender
As
Object
, e
As
GridViewCellEventArgs)
Handles
grdSales.CellValueChanged
'count checked rows, enable process, set label
If
e.ColumnIndex > 0
Then
Return
'only want checked col [0]
Dim
iCnt
As
Integer
= fCountCheckedRows()
btnProcess.Enabled = iCnt > 0
lblCheckedRecords.Text =
CStr
(lblCheckedRecords.Tag) & iCnt
End
Sub
Private
Function
fCountCheckedRows()
As
Integer
Dim
iCount
As
Integer
= 0
For
i = 0
To
grdSales.MasterTemplate.DataView.ItemCount - 1
If
Not
IsDBNull(grdSales.Rows(i).Cells(0).Value)
Then
If
grdSales.Rows(i).Cells(0).Value =
True
Then
iCount += 1
End
If
Next
Return
iCount
End
Function
7 Answers, 1 is accepted
Thank you for writing.
As you have already found out, the appropriate way to count all checked rows is to iterate the rows and determine their checked state. The HeaderCellToggleStateChanged event is the appropriate way to iterate all rows and get which are the checked ones. Here is a sample code snippet:
Private
Sub
HeaderCellToggleStateChanged(sender
As
Object
, e
As
GridViewHeaderCellEventArgs)
GetCheckedRows()
End
Sub
Private
Sub
GetCheckedRows()
Dim
iCount
As
Integer
= 0
For
i = 0
To
Me
.RadGridView1.MasterTemplate.DataView.ItemCount - 1
If
Not
IsDBNull(
Me
.RadGridView1.Rows(i).Cells(0).Value)
Then
If
Me
.RadGridView1.Rows(i).Cells(2).Value =
True
Then
iCount += 1
End
If
Next
RadMessageBox.Show(
"Checked: "
& iCount)
End
Sub
I hope this information helps. Should you have further questions I would be glad to help.
Regards,
Dess
Progress Telerik

Thanks. I moved the Counter to the HeaderCellToggledChanged , but the problem is the change detection occurs before the rows are checked. For example
- no rows in a grid are checked,
- I check the header checkbox which triggers the HeaderCellToggledChanged event.
- This starts the counter function which gets the checked value of each row, BUT the check has nbot been registered yet, so non of the rows are reported as checked.
- After the HeaderCellToggledChanged is exited the rowcount is 0 and then all items are checked
- If I uncheck the header, the rowcount is reported as All, but none are checked (as exoected) when the HeaderCellToggledChanged is completed.
Somehow I need to run the CountCheckedRows after the HeaderCellToggledChanged has actually checked the rows
Thank you for writing back.
Indeed, in the HeaderCellToggleStateChanged event, the rows in the grid are not changed yet. However, you can use the inversed value of the header cell. Here is a sample code snippet:
Private
Sub
HeaderCellToggleStateChanged(sender
As
Object
, e
As
GridViewHeaderCellEventArgs)
If
e.State = Enumerations.ToggleState.
On
Then
GetCheckedRows(ToggleState.Off)
Else
GetCheckedRows(ToggleState.Off)
End
If
End
Sub
Private
Sub
GetCheckedRows(toggleState
As
ToggleState)
Dim
iCount
As
Integer
= 0
For
i = 0
To
Me
.RadGridView1.MasterTemplate.DataView.ItemCount - 1
If
Not
IsDBNull(
Me
.RadGridView1.Rows(i).Cells(0).Value)
Then
If
Me
.RadGridView1.Rows(i).Cells(2).Value = toggleState
Then
iCount += 1
End
If
Next
RadMessageBox.Show(
"Checked: "
& iCount)
End
Sub
I hope this information helps. If you have any additional questions, please let me know.
Regards,
Dess
Progress Telerik

Thanks for the help Dess, but I think the the fix is too cumbersome. If I have some rows checked and then click the header toggle, the count will be wrong because some are checked others are not, but all will be checked after the HeaderCellState has finished.
In addition I want to keep track of the checked count each time a single row is checked, and am using the CellChange event for that. The problem is that the HeaderCellToggle causes the CellChange to fire which calls the iterative RowCount almost recursively since the count is performed on every row change.
Ideally there would be a HeaderCellToggleStateChanging event where I could set a boolean so that the CellChange would not fire the RowCount and a HeaderCellToggleStateChanged event where the rows were changed within the event. Since that doesn't seem to be possible and an intrinsic count property is not available, I don't think the idea of keeping track of checked rows will work. Unless you know how to address the issues I've laid out.
I'm sure I'm not the first person looking to keep count of checked rows, so maybe I'm missing something?
Thanks, Brendan
Thank you for writing back.
It is absolutely normal that the CellValueChanged event is fired for each cell when you toggle the header checkbox because the header checkbox actually updates all cells belonging to this column. I have prepared a sample code snippet which achieves the desired behavior:
Sub
New
()
InitializeComponent()
Dim
dt
As
New
DataTable
dt.Columns.Add(
"Id"
,
GetType
(
Integer
))
dt.Columns.Add(
"Name"
,
GetType
(
String
))
dt.Columns.Add(
"IsActive"
,
GetType
(
Boolean
))
For
index = 1
To
1000
dt.Rows.Add(index,
"Row"
& index,
False
)
Next
Me
.RadGridView1.DataSource = dt
Me
.RadGridView1.AutoSizeColumnsMode = Telerik.WinControls.UI.GridViewAutoSizeColumnsMode.Fill
Dim
checkBoxColumn
As
GridViewCheckBoxColumn = TryCast(
Me
.RadGridView1.Columns.Last(), GridViewCheckBoxColumn)
checkBoxColumn.EnableHeaderCheckBox =
True
AddHandler
Me
.RadGridView1.CellValueChanged,
AddressOf
CellValueChanged
AddHandler
Me
.RadGridView1.MouseDown,
AddressOf
GridMouseDown
AddHandler
Me
.RadGridView1.MouseUp,
AddressOf
GridMouseUp
End
Sub
Private
Sub
GridMouseDown(sender
As
Object
, e
As
MouseEventArgs)
Dim
checkBox
As
RadCheckBoxElement = TryCast(
Me
.RadGridView1.ElementTree.GetElementAtPoint(e.Location), RadCheckBoxElement)
If
checkBox IsNot
Nothing
Then
Dim
headerCell
As
GridCheckBoxHeaderCellElement = TryCast(checkBox.Parent, GridCheckBoxHeaderCellElement)
If
headerCell isnot
Nothing
Then
RemoveHandler
Me
.RadGridView1.CellValueChanged,
AddressOf
CellValueChanged
End
If
End
If
End
Sub
Private
Sub
GridMouseUp(sender
As
Object
, e
As
MouseEventArgs)
Dim
checkBox
As
RadCheckBoxElement = TryCast(
Me
.RadGridView1.ElementTree.GetElementAtPoint(e.Location), RadCheckBoxElement)
If
checkBox IsNot
Nothing
Then
Dim
headerCell
As
GridCheckBoxHeaderCellElement = TryCast(checkBox.Parent, GridCheckBoxHeaderCellElement)
If
headerCell IsNot
Nothing
Then
GetCheckedRows()
AddHandler
Me
.RadGridView1.CellValueChanged,
AddressOf
CellValueChanged
End
If
End
If
End
Sub
Private
Sub
GetCheckedRows()
Dim
iCount
As
Integer
= 0
For
i = 0
To
Me
.RadGridView1.MasterTemplate.DataView.ItemCount - 1
If
Not
IsDBNull(
Me
.RadGridView1.Rows(i).Cells(0).Value)
Then
If
Me
.RadGridView1.Rows(i).Cells(2).Value =
True
Then
iCount += 1
End
If
Next
RadMessageBox.Show(
"Checked: "
& iCount)
End
Sub
Private
Sub
CellValueChanged(sender
As
Object
, e
As
GridViewCellEventArgs)
GetCheckedRows()
End
Sub
I hope this information helps. If you have any additional questions, please let me know.
Regards,
Dess
Progress Telerik

Thanks Tess for the detailed reply. I found a simpler way to do this, though I may have found a bug in the grid. Here is the revised code. Since the HeaderToggle either checks All or No rows, I just return that number. For an individual row click I use the CellEndEdit event which fires after the checkbox update.
Where I believe a bug may exist is when using filters. For example I have a grid with 15 rows. I check the HeaderCell to check all rows - works fine. I then apply an Excel filter to keep say 5 rows - they remain checked. If uncheck/recheck a single row, the HeaderCellToggleStateChanged may fire - randomly at that. Maybe 3 of the 5 rows being checked will fire the HeaderEvent, I'm not sure why, but I see it in debug.
However, it all works out with my code since I do not iterate rows on the HeaderCellToggleStateChanged event. On top of that the CellEndEdit fires after the HeaderCellToggleStateChanged event, so I get a correct checked row count in the end.
I hope this helps someone with counting checked rows in a grid :-)
Private
Function
fCountCheckedRows()
As
Integer
Dim
iCount
As
Integer
= 0
Dim
iRows
As
Integer
= RadGrid.MasterTemplate.DataView.ItemCount
For
i = 0
To
iRows - 1
If
CBool
(RadGrid.MasterTemplate.DataView.Item(i).Cells(0).Value) =
True
Then
iCount += 1
Next
Return
iCount
End
Function
Private
Sub
RadGrid_HeaderCellToggleStateChanged(sender
As
Object
, e
As
GridViewHeaderCellEventArgs)
Handles
RadGrid.HeaderCellToggleStateChanged
If
e.State = Enumerations.ToggleState.
On
Then
'Simply return row count
lblCheckedRecords.Text =
CStr
(lblCheckedRecords.Tag) & RadGrid.MasterTemplate.DataView.ItemCount
Else
'unchecked, report 0
lblCheckedRecords.Text =
CStr
(lblCheckedRecords.Tag) &
"0"
End
If
End
Sub
Private
Sub
RadGrid_CellEndEdit(sender
As
Object
, e
As
GridViewCellEventArgs)
Handles
RadGrid.CellEndEdit
If
e.Column.Index = 0
Then
'checkbox column
pSetCheckedRowsLabel()
End
If
End
Sub
Private
Sub
RadGrid_FilterChanged(sender
As
Object
, e
As
GridViewCollectionChangedEventArgs)
Handles
RadGrid.FilterChanged
pSetCheckedRowsLabel()
End
Sub
Private
Sub
pSetCheckedRowsLabel()
Dim
iCnt
As
Integer
= fCountCheckedRows()
lblCheckedRecords.Text =
CStr
(lblCheckedRecords.Tag) & iCnt.ToString
End
Sub
Thank you for writing back.
I am glad that you have found a suitable solution for your scenario. Note that the header checkbox should display a valid state considering the available rows. If you filter some of the rows, they won't be visible. As a result, the toggle state of the header may need to be updated. That is why the HeaderCellToggleStateChanged will be fired in this case. Feel free to use your code which handles the custom scenario that you have.
I hope this information helps. If you have any additional questions, please let me know.
Regards,
Dess
Progress Telerik