This is a migrated thread and some comments may be shown as answers.

How to count checked items

7 Answers 665 Views
GridView
This is a migrated thread and some comments may be shown as answers.
Brendan
Top achievements
Rank 1
Brendan asked on 09 Jun 2017, 04:51 PM

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;

  1. Is there a CheckedCount method instead of iterating?  would have to work on Excel filtered grid
  2. Is there a way to 'wait' for the header toggle to finish so that the CellValueChanged could be set not to trigger until done?
  3. 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

Sort by
0
Dess | Tech Support Engineer, Principal
Telerik team
answered on 12 Jun 2017, 09:48 AM
Hello Brendan, 

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
Try our brand new, jQuery-free Angular 2 components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
0
Brendan
Top achievements
Rank 1
answered on 12 Jun 2017, 02:33 PM

Thanks.  I moved the Counter to the HeaderCellToggledChanged , but the problem is the change detection occurs before the rows are checked.  For example

  1. no rows in a grid are checked,
  2. I check the header checkbox which triggers the HeaderCellToggledChanged event.  
  3. 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.
  4. After the HeaderCellToggledChanged is exited the rowcount is 0 and then all items are checked
  5. 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

0
Dess | Tech Support Engineer, Principal
Telerik team
answered on 13 Jun 2017, 08:52 AM
Hello Brendan, 

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
Try our brand new, jQuery-free Angular 2 components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
0
Brendan
Top achievements
Rank 1
answered on 13 Jun 2017, 02:42 PM

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

0
Dess | Tech Support Engineer, Principal
Telerik team
answered on 14 Jun 2017, 08:12 AM
Hello 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
Try our brand new, jQuery-free Angular 2 components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
0
Brendan
Top achievements
Rank 1
answered on 14 Jun 2017, 03:23 PM

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
0
Accepted
Dess | Tech Support Engineer, Principal
Telerik team
answered on 15 Jun 2017, 10:14 AM
Hello Brendan, 

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
Try our brand new, jQuery-free Angular 2 components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
Tags
GridView
Asked by
Brendan
Top achievements
Rank 1
Answers by
Dess | Tech Support Engineer, Principal
Telerik team
Brendan
Top achievements
Rank 1
Share this question
or