MultiSelect Rows

9 posts, 0 answers
  1. nhalm
    nhalm avatar
    8 posts
    Member since:
    Jun 2010

    Posted 12 Sep 2011 Link to this post

    I am currently using the radgridview to hold 246k rows and 13 columns of data.  I extened the radgridview so that I could handle many othe requiremnts that I have.  Even before extending the control I have noticed significant problems in selecting multiple rows.  For instance in the application I just described there is not the much data present in the grid.  To get the data I progromatically build a datatable and then attach it to the grid as the datasource.  Then I try to select multiple rows via shift + click and it will take over 25 seconds to select 100k rows.  I am not sure what is exactly causing this problem but to make things even worse when I am done with my selection and would like to lose it or make another one it takes just as long for the grid do remove the selection from the selectedrows property.  Is there anything that I can do to make this simple process respond in a reasonable manner?

    I am using winforms controls 2011.1.11.419 and visual studio 2010 vb.net
  2. Nikolay
    Admin
    Nikolay avatar
    1803 posts

    Posted 13 Sep 2011 Link to this post

    Hello Nick,

    Thank you for bringing our attention to this issue.

    Yes, I can confirm that multiple row selection can become slow when there is a large number of rows. I added this issue in our issue tracking system and it will be addressed in our upcoming release. I updated also your Telerik points for reporting it.

    Due to the nature of the issue I am not able to provide a suitable work around. However, if this is a show stopper for you we could prepare an internal build where the issue is addressed. 

    Should you have any other questions, do not hesitate to contact us.
     
    Regards,
    Nikolay
    the Telerik team

    Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

  3. UI for WinForms is Visual Studio 2017 Ready
  4. nhalm
    nhalm avatar
    8 posts
    Member since:
    Jun 2010

    Posted 13 Sep 2011 Link to this post

    Thank you for the reply.  I actually updated to q2 2011 last night and the seems to work a slightly better.  From what I can tell the row collection is just iterated through and the .isSelected flag is set on the gridrowinfo.  I can see how it is possible for this to be slow but this actually brings up another issue that I have been having with the gridview.  My other issue is iterating the the rowcollection is quite slow.  I have tried both For i as Integer =0 to grd.rows.count-1 and also for each row in grd.rows and I can say the performance seems improved using For i as Integer.  
    It would be easier if I had the option to get the datasource table rows and manipulate them, and probably faster.  I know I can use the databounditem property of the row but that is still requiring me to iterated through the selected rows.

    I don't really know the answer I just know that manipulating large amounts of data through gridfunctions is very slow.
  5. Jack
    Admin
    Jack avatar
    2335 posts

    Posted 15 Sep 2011 Link to this post

    Hi Nick Halm,

    Thank you for writing me back.

    Setting the IsSelected property causes RadGridView to update other row states and that is what causes the issue. As I said, we will optimize the update process in our upcoming release. Our fix will affect the selection operations when using the user interface. For example when clicking a row with the mouse.

    I tested the following case: a RadGridView containing 400 000 rows. My test indicates that iterating the Rows collection takes ~582 milliseconds. However, this process may become slow when updating cell values or when you change the IsSelected property. If this is the case, I recommend the following approach:
    For i As Integer = 0 To Me.radGridView1.ChildRows.Count - 1
        Dim row As GridViewRowInfo = Me.radGridView1.ChildRows(i)
        row.SuspendPropertyNotifications()
        row.IsSelected = True
        row.ResumePropertyNotifications()
    Next
     
    Me.radGridView1.TableElement.ViewElement.ClearRows()
    Me.radGridView1.TableElement.ViewElement.UpdateRows()

    If this does not help, please send me your code and I will try to optimize it.

    I am looking forward to your reply.
     
    Regards,
    Jack
    the Telerik team

    Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

  6. nhalm
    nhalm avatar
    8 posts
    Member since:
    Jun 2010

    Posted 19 Sep 2011 Link to this post

    Thank you again for your help.  I have tried your solution to try and speed up the problem that I have been experiencing.  I would like to send you some code with this reply to see what you think of my solution and if there are any glaring problems.  First of all I should be more specific of what I am trying to accomplish.  I need the grid to do a few more things including, highlight all changes, keep track of all modified, added, deleted, and error rows and use custom row formatting and filters to display them.  I also have the requirement to handle copy and past from one grid to another or to the same grid, and to be able to change one column on selected rows simultaneously.  To accomplish all these things I have simply inherited the RadGridView, added custom properties, functions and handlers.  One thing that I found along the way is that selecting multiple rows is slow, I am happy to report that after updating to version 2011 Q2 that speed is actually dramatically improved, so that is no longer one of my concerns.  I believe that I have an acceptable solution to the copy and paste requirement and row/cell formatting.  Right now I am still trying to handle two problems.  The first is changing the same column on selected rows.  The fastest way I have found to do this is to actually add a column to the datasource table so that I can easily refernce that specific row outside of the grid itself.  Included is some code to do that but basically what I do is in the Datasource property before it gets set I add some columns to the table and then enumerate the column rowid.  Then in the CellValueChanged event I look to see if more that one row is selected and if so change each column to the same value.  The last step that takes care of making the cell formatting possible is the Datasource.ColumnChanged event that adds the original value and changed cell index into an object column that holds a hash table.  I don't know if this makes that much sense or not, but simply iterating the RowsSelectedCollection and changing those values was taking about 50 milliseconds per cell when the selected rows collection is about 50k rows and the datasource is 250k rows.  I can actually live with this solution but there is on more problem that it brings up.  How do I keep that form from flickering or going white completely during a long running event like this?  Preferably I would love to have a progress bar for something that is going to take this long.  Although roughyl 38 seconds to change 50k rows doesn't seem like much, that does not include any of the time it takes to commit and audit each change to a database, that is why I am looking to save milliseconds and reduce screen lag.  Thanks again for any help.
    Public Shadows Property DataSource() As Object
        Get
            Return MyBase.DataSource
        End Get
        Set(ByVal value As Object)
            If value Is Nothing Then
                MyBase.DataSource = Nothing
                Exit Property
            End If
     
            Dim table = TryCast(value, DataTable)
     
            If table Is Nothing Then
                MyBase.DataSource = value
                Exit Property
            End If
     
            If Not table.Columns.Contains(conADDED) Then
                table.Columns.Add(conADDED, System.Type.GetType("System.String"))
            End If
            If Not table.Columns.Contains(conDELETED) Then
                table.Columns.Add(conDELETED, System.Type.GetType("System.String"))
            End If
            If Not table.Columns.Contains(conMODIFIED) Then
                table.Columns.Add(conMODIFIED, System.Type.GetType("System.String"))
            End If
            If Not table.Columns.Contains(conERRORROW) Then
                table.Columns.Add(conERRORROW, System.Type.GetType("System.String"))
            End If
            If Not table.Columns.Contains(conCHANGESLINK) Then
                table.Columns.Add(conCHANGESLINK, System.Type.GetType("System.Object"))
            End If
            If Not table.Columns.Contains(conROWID) Then
                table.Columns.Add(conROWID, System.Type.GetType("System.Int32"))
            End If
     
            For i As Integer = 0 To table.Rows.Count - 1
                table.Rows(i).Item(conROWID) = i
            Next
     
            AddHandler table.ColumnChanging, AddressOf Datasource_ColumnChanging
     
            MyBase.DataSource = table
     
     
        End Set
     
    End Property
    Private Sub Datasource_ColumnChanging(ByVal sender As Object, ByVal e As DataColumnChangeEventArgs)
           Dim tim As New Stopwatch
           tim.Start()
     
           If _skipColumns.Contains(e.Column.ColumnName) Then Exit Sub
           Dim hTable As Hashtable
           hTable = TryCast(e.Row.Item(conCHANGESLINK), Hashtable)
     
           If hTable Is Nothing Then
               hTable = New Hashtable
               hTable.Add(e.Column.ColumnName, e.Row.Item(e.Column))
               e.Row.Item(conCHANGESLINK) = hTable
               e.Row.Item(conMODIFIED) = conCHFLAG
           Else
               If hTable.Contains(e.Column.ColumnName) = False Then
                   hTable.Add(e.Column.ColumnName, e.Row.Item(e.Column))
                   e.Row.Item(conCHANGESLINK) = hTable
                   e.Row.Item(conMODIFIED) = conCHFLAG
               Else
                   If e.ProposedValue = hTable.Item(e.Column.ColumnName) Then
                       hTable.Remove(e.Column.ColumnName)
                       If hTable.Count = 0 Then
                           e.Row.Item(conMODIFIED) = conUCHFLAG
                       End If
                   End If
               End If
           End If
     
           tim.Stop()
           Debug.Print("time to change one cell = {0}:{1}:{2}", tim.Elapsed.Minutes, tim.Elapsed.Seconds, tim.Elapsed.Milliseconds)
       End Sub
    Private Sub lsGridView_CellValueChanged(sender As Object, e As Telerik.WinControls.UI.GridViewCellEventArgs) Handles Me.CellValueChanged
           If _isUpdating Then Exit Sub
     
           If Me.SelectedRows.Count > 1 Then
               RemoveHandler Me.CellValueChanged, AddressOf lsGridView_CellValueChanged
               _isUpdating = True
               UseWaitCursor = True
     
               Dim colname As String = e.Column.FieldName
               Dim indexes(Me.SelectedRows.Count - 1) As Integer
     
               For i As Integer = 0 To Me.SelectedRows.Count - 1
                   indexes(i) = Me.SelectedRows(i).Cells(conROWID).Value
               Next
     
               Dim oldPK As DataColumn() = DataSource.primarykey
               Dim rw As DataRow
     
               DataSource.primarykey = New DataColumn() {DataSource.columns(conROWID)}
     
               Dim tim As New Stopwatch
               tim.Start()
     
               Me.BeginUpdate()
               For k As Integer = 0 To indexes.Count - 1
                   rw = DataSource.rows.find(indexes(k))
                   rw.Item(colname) = e.Value
               Next
               Me.EndUpdate()
     
               
               Me.TableElement.ViewElement.ClearRows()
               Me.TableElement.ViewElement.UpdateRows()
     
               tim.Stop()
               Debug.Print("time to change all = {0}:{1}:{2}", tim.Elapsed.Minutes, tim.Elapsed.Seconds, tim.Elapsed.Milliseconds)
     
               DataSource.primarykey = oldPK
     
               _isUpdating = False
               UseWaitCursor = False
     
               AddHandler Me.CellValueChanged, AddressOf lsGridView_CellValueChanged
           End If
     
       End Sub
  7. nhalm
    nhalm avatar
    8 posts
    Member since:
    Jun 2010

    Posted 19 Sep 2011 Link to this post

    I actually took the timer out of the Datasource_ColumnsChanging Event and the speed dropped dramatically. to just less than 10 seconds to update 48k rows.  I shouldn't have left that in there I guess.  Thanks for your help.

    Nick
  8. nhalm
    nhalm avatar
    8 posts
    Member since:
    Jun 2010

    Posted 21 Sep 2011 Link to this post

    Is there any update on this?
    I have found that while ctrl-A to select all rows is quite fast now using the shift-click method is still incredibly slow taking almost a minutes to select 70k rows.  The other problem is that even after making my selection it still takes a long time to un-select those rows.  I really need some help on this.  I tried your example using the SuspendPropertyNotification but, since I am not programatically selecting rows outside the grid I don't know how I could make that work.  
  9. nhalm
    nhalm avatar
    8 posts
    Member since:
    Jun 2010

    Posted 21 Sep 2011 Link to this post

    I have found a way to removed the selectedrows quickly now.  This is what I am using for that:

    Private Sub lsGridView_CurrentRowChanging(sender As Object, e As Telerik.WinControls.UI.CurrentRowChangingEventArgs) Handles Me.CurrentRowChanging
        If Me.SelectedRows.Count > 1 Then
            For i As Integer = 0 To Me.ChildRows.Count - 1
                Dim row As Telerik.WinControls.UI.GridViewRowInfo = Me.ChildRows(i)
                row.SuspendPropertyNotifications()
                row.IsSelected = False
                row.ResumePropertyNotifications()
            Next
     
            Me.TableElement.ViewElement.ClearRows()
            Me.TableElement.ViewElement.UpdateRows()
        Else
            If My.Computer.Keyboard.ShiftKeyDown AndAlso Me.SelectedRows.Count = 1 Then
                Dim rstart As Integer
                Dim rend As Integer
     
                If e.CurrentRow.Index > e.OldRow.Index Then
                    rstart = e.NewRow.Index
                    rend = e.CurrentRow.Index
                Else
                    rstart = e.CurrentRow.Index
                    rend = e.NewRow.Index
                End If
     
                For i As Integer = rstart To rend
                    Dim row As Telerik.WinControls.UI.GridViewRowInfo = Me.ChildRows(i)
                    row.SuspendPropertyNotifications()
                    row.IsSelected = True
                    row.ResumePropertyNotifications()
                Next
     
                Me.TableElement.ViewElement.ClearRows()
                Me.TableElement.ViewElement.UpdateRows()
     
            End If
     
        End If
    End Sub

    The first part works great.  The second part is not so great.  I am trying to speed up the shift_click method of selecting rows and altought this second part iterates through the rows very quickly the selectedRowsCollection does not update.  After the operation Me.SelectedRows.Count = 1.  If I remove the row.suspendPropertyNotifications and the subsequent call it will update the collection but the speed is nothing to be proud of.  

    Also I still need a way to prevent the application from flickering and turning white or worse, throwing the "This Application has stopped responding." message.  

    Thanks for any help!

    Nick
  10. Svett
    Admin
    Svett avatar
    728 posts

    Posted 22 Sep 2011 Link to this post

    Hello Nick,

    Thank you for the description of your issue and for the code. I am not sure that I fully understand what you want to achieve, but I looked into your code and noticed you are applying very often the following code snippet:
    Me.TableElement.ViewElement.ClearRows()
    Me.TableElement.ViewElement.UpdateRows()

    Calling the ClearRows and UpdateRows methods causes RadGridView to reset all of its visual elements. The same is valid for the EndUpdate method. It is not necessary to call these methods and you could change the code the following way:
    Me.BeginUpdate()
    'Your update operation here
    Me.EndUpdate(False)
    Me.RadGridView1.TableElement.Update(GridUINotifyAction.DataChanged)

    If your requirement is to change other column values based on the changed value, you could optimize this code further:
    Me.BeginUpdate()
    For k As Integer = 0 To indexes.Count - 1
        Me.SelectedRows(k).Cells(conROWID).Value = e.Value
    Next
    Me.EndUpdate(False)
    Me.RadGridView1.TableElement.Update(GridUINotifyAction.DataChanged)

    Here is the modified version of your last code snippet:
    Private Sub lsGridView_CurrentRowChanging(sender As Object, e As Telerik.WinControls.UI.CurrentRowChangingEventArgs) Handles Me.CurrentRowChanging
        If Me.SelectedRows.Count > 1 Then
     
            Me.BeginUpdate()
     
            For i As Integer = 0 To Me.ChildRows.Count - 1
                Dim row As Telerik.WinControls.UI.GridViewRowInfo = Me.ChildRows(i)
                row.SuspendPropertyNotifications()
                row.IsSelected = False
                row.ResumePropertyNotifications()
            Next
      
              Me.EndUpdate(False)
        Me.RadGridView1.TableElement.Update(GridUINotifyAction.DataChanged)
        Else
            If My.Computer.Keyboard.ShiftKeyDown AndAlso Me.SelectedRows.Count = 1 Then
                Dim rstart As Integer
                Dim rend As Integer
      
                If e.CurrentRow.Index > e.OldRow.Index Then
                    rstart = e.NewRow.Index
                    rend = e.CurrentRow.Index
                Else
                    rstart = e.CurrentRow.Index
                    rend = e.NewRow.Index
                End If
      
                Me.BeginUpdate()
     
                For i As Integer = rstart To rend
                    Dim row As Telerik.WinControls.UI.GridViewRowInfo = Me.ChildRows(i)
                    row.SuspendPropertyNotifications()
                    row.IsSelected = True
                    row.ResumePropertyNotifications()
                Next
      
                Me.EndUpdate(False)
               Me.RadGridView1.TableElement.Update(GridUINotifyAction.DataChanged)
      
            End If
      
        End If
    End Sub

    Now it should run much faster. Could you please confirm that? If you continue to experience a slow down, please open a new support ticket, send me your application and I will try to optimize it.

    I am looking forward to your reply.
     
    Best wishes,
    Svett
    the Telerik team

    Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

Back to Top
UI for WinForms is Visual Studio 2017 Ready