Запустил, сделал у себя - действительно медленнее программное выделение, чем через GUI
Стало дико интересно, что там за кривая реализация...
Бродя Рефлектором по коду выяснил, что
только приватные секции реализующие выделение инкриминируют переменную
Me.inBulkPaintCount += 1 и потом сбрасывают
Me.ExitBulkPaint(-1, -1)Что в свою очередь в событии
OnCellStateChanged не даёт вызывать
Me.InvalidateCellPrivate- Код: Выделить всё
Protected Overridable Sub OnCellStateChanged(ByVal e As DataGridViewCellStateChangedEventArgs)
'...
If ((e.StateChanged = DataGridViewElementStates.Selected) AndAlso (Me.inBulkPaintCount = 0)) Then
Me.InvalidateCellPrivate(dataGridViewCell)
End If
'...
End Sub
Т.е. при обычном выделении ячейки, каждый раз вызывается
Me.InvalidateCellPrivate, а при выделении мышкой - только в конце
Написал класс-обёртку с функой выделения (хак через рефлексию для доступа к приватной функе
SelectCellRange ) + двойная буферизация
- Код: Выделить всё
Public Class DGV : Inherits DataGridView
Public Sub New()
MyBase.New()
MyBase.DoubleBuffered = True
End Sub
Public Sub SelectCellRange(ByVal columnIndexFrom As Integer, ByVal rowIndexFrom As Integer, ByVal columnIndexTo As Integer, ByVal rowIndexTo As Integer, ByVal [select] As Boolean)
Dim mi As MethodInfo = GetType(DataGridView).GetMethod("SelectCellRange", System.Reflection.BindingFlags.NonPublic Or System.Reflection.BindingFlags.Instance)
mi.Invoke(Me, New Object() {columnIndexFrom, rowIndexFrom, columnIndexTo, rowIndexTo, [select]})
End Sub
End Class
Заполнил DGV ячейками 0...500 и строками 0...500
Протестировал выделение всего диапазона функами:
- Код: Выделить всё
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim Timer As New System.Diagnostics.Stopwatch
Timer.Start()
DGV.SelectCellRange(0, 0, 500, 500, True)
Timer.Stop()
Console.WriteLine("Reflexion: " & Timer.ElapsedMilliseconds)
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim i As Integer
Dim j As Integer
Dim Timer As New System.Diagnostics.Stopwatch
Timer.Start()
For i = 0 To 500
For j = 0 To 500
DGV.Item(i, j).Selected = True
Next
Next
Timer.Stop()
Console.WriteLine("Item.Select: " & Timer.ElapsedMilliseconds)
End Sub
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
Dim Timer As New System.Diagnostics.Stopwatch
Timer.Start()
DGV.SelectAll()
Timer.Stop()
Console.WriteLine("SelectAll: " & Timer.ElapsedMilliseconds)
End Sub
Результат в миллисекундах:
- Код: Выделить всё
Reflexion: 241
Reflexion: 256
Reflexion: 199
Item.Select: 422
Item.Select: 468
Item.Select: 418
SelectAll: 171
SelectAll: 180
SelectAll: 158
Почти идеально, время сократилось почти в 2 раза. Ещё можно вынести в тело класса получение MethodInfo, что ещё чуток понизит время выполнения
Думаю, что средствами DataGridView большей скорости не добиться, т.е. либо делать свои костыли, либо искать другие аналоги контрола
P.S. Я сделал на VB, но думаю не составит труда перевести на веб-сервисе в C#
Птицей Гермеса меня называют, свои крылья пожирая... сам себя я укрощаю
私はヘルメスの鳥 私は自らの羽根を喰らい 飼い慣らされる