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

MultiSelect Rows

8 Answers 325 Views
GridView
This is a migrated thread and some comments may be shown as answers.
nhalm
Top achievements
Rank 1
nhalm asked on 12 Sep 2011, 05:09 PM
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

8 Answers, 1 is accepted

Sort by
0
Nikolay
Telerik team
answered on 13 Sep 2011, 11:19 AM
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 >>

0
nhalm
Top achievements
Rank 1
answered on 13 Sep 2011, 04:14 PM
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.
0
Jack
Telerik team
answered on 15 Sep 2011, 08:50 AM
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 >>

0
nhalm
Top achievements
Rank 1
answered on 20 Sep 2011, 03:56 AM
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
0
nhalm
Top achievements
Rank 1
answered on 20 Sep 2011, 04:06 AM
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
0
nhalm
Top achievements
Rank 1
answered on 21 Sep 2011, 04:49 PM
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.  
0
nhalm
Top achievements
Rank 1
answered on 21 Sep 2011, 07:05 PM
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
0
Svett
Telerik team
answered on 22 Sep 2011, 05:11 PM
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 >>

Tags
GridView
Asked by
nhalm
Top achievements
Rank 1
Answers by
Nikolay
Telerik team
nhalm
Top achievements
Rank 1
Jack
Telerik team
Svett
Telerik team
Share this question
or