2016-12-09 2 views
1

У меня есть класс Part с списком полей в приведенном ниже коде. У меня есть элемент управления DataGridView, который я фильтрую с помощью DLL Advanced DGV (ADGV) из NUGET. Я должен включить ADGV в свою winform. В настоящее время у меня есть DataGridView, окно поиска в форме и кнопка для запуска следующей функции. Мне нужно пройти через все видимые строки, собрать уникальный список номеров деталей с их самыми последними изменениями, а затем покрасить строки в DataGridView, устаревшие, проверив номер детали и оборот каждой строки в списке mostuptodate , Для 45 000 записей, отображаемых в DataGridView, это займет ~ 17 секунд. Для ~ 50 записей требуется ~ 1,2 секунды. Это крайне неэффективно, но я не вижу способа сократить время.Выделите DataGridViewRows на основе сравнения значений с другими строками

Sub highlightOutdatedParts() 
    'Purpose: use the results in the datagridview control, find the most recent revision of each part, and 
    '   highlight all outdated parts relative to their respective most recent revisions 
    'SORT BY PART NUMBER AND THEN BY REV 
    If resultsGrid.ColumnCount = 0 Or resultsGrid.RowCount = 0 Then Exit Sub 
    Dim stopwatch As New Stopwatch 
    stopwatch.Start() 
    resultsGrid.Sort(resultsGrid.Columns("PartNumber"), ListSortDirection.Ascending) 
    Dim iBag As New ConcurrentBag(Of Part) 
    Dim sortedList As Generic.List(Of Part) 
    For Each row As DataGridViewRow In resultsGrid.Rows 
     If row.Visible = True Then 
      Dim iPart As New Part() 
      Try 
       iPart.Row = row.Cells(0).Value 
       iPart.Workbook = CStr(row.Cells(1).Value) 
       iPart.Worksheet = CStr(row.Cells(2).Value) 
       iPart.Product = CStr(row.Cells(3).Value) 
       iPart.PartNumber = CStr(row.Cells(4).Value) 
       iPart.ItemNo = CStr(row.Cells(5).Value) 
       iPart.Rev = CStr(row.Cells(6).Value) 
       iPart.Description = CStr(row.Cells(7).Value) 
       iPart.Units = CStr(row.Cells(8).Value) 
       iPart.Type = CStr(row.Cells(9).Value) 
       iPart.PurchCtgy = CStr(row.Cells(10).Value) 
       iPart.Qty = CDbl(row.Cells(11).Value) 
       iPart.TtlPerProd = CDbl(row.Cells(12).Value) 
       iPart.Hierarchy = CStr(row.Cells(13).Value) 
       iBag.Add(iPart) 
      Catch ice As InvalidCastException 
      Catch nre As NullReferenceException 
      End Try 
     End If 
    Next 
    sortedList = (From c In iBag Order By c.PartNumber, c.Rev).ToList() ' sort and convert to list 
    Dim mostUTDRevList As New Generic.List(Of Part)  ' list of most up to date parts, by Rev letter 
    For sl As Integer = sortedList.Count - 1 To 0 Step -1 'start at end of list and work to beginning 
     Dim query = From entry In mostUTDRevList ' check if part number already exists in most up to date list 
        Where entry.PartNumber = sortedList(sl).PartNumber 
        Select entry 
     If query.Count = 0 Then  ' if this part does not already exist in the list, add. 
      mostUTDRevList.Add(sortedList(sl)) 
     End If 
    Next 
    'HIGHLIGHT DATAGRIDVIEW ROWS WHERE PART NUMBERS ARE OUT OF DATE 
    For Each row As DataGridViewRow In resultsGrid.Rows 
     ' if that part with that Rev does not exist in the list, it must be out of date 
     Try 
      Dim rowPN As String = CStr(row.Cells(4).Value).ToUpper ' get part number 
      Dim rowR As String = CStr(row.Cells(6).Value).ToUpper ' get Rev 
      Dim query = From entry In mostUTDRevList ' check if that part number with that Rev is in the list. 
         Where entry.PartNumber.ToUpper.Equals(rowPN) AndAlso 
         entry.Rev.ToUpper.Equals(rowR) 
         Select entry 
      If query.Count = 0 Then  ' if the part is out of date highlight its' row 
       row.DefaultCellStyle.BackColor = Color.Chocolate 
      End If 
     Catch ex As NullReferenceException 
     Catch ice As InvalidCastException 
     End Try 
    Next 
    resultsGrid.Select() 
    stopwatch.Stop() 
    If Not BackgroundWorker1.IsBusy() Then timertextbox.Text = stopwatch.Elapsed.TotalSeconds.ToString & " secs" 
    MessageBox.Show("Highlighting completed successfully.") 
End Sub 
+0

мне очень интересно, почему это с помощью 'ConcurrentBag' Его всегда быстрее работать с данными, чем данные рыбы из-под контроля и гипсе это к типу. Я играл с вашей предыдущей версией, и часть проблемы запускает эти запросы в цикле для всех строк, независимо от того, видны они или нет (в этом смысле это похоже на другое). – Plutonix

ответ

1

Практически всегда быстрее работать с данными, чем с контролем. Элемент управления представляет собой просто средство для представления данных (в сетке )). Работа с данными оттуда требует слишком большого преобразования, чтобы быть эффективным. Затем, используя события DGV, чтобы выделить строки

Сложно сказать, все детали того, что вы делаете, но это выглядит как вы сравниваете данные по себе (в отличие от какой-либо конкретной таблицы, где Lastest коды пересмотра). Также не понятно, почему источники данных представляют собой коллекции, ConcurrentBags и т. Д. Ключевым моментом будет использование коллекций, оптимизированных для работы.

Чтобы продемонстрировать, у меня есть таблица с 75 000 строк; коды продуктов выбираются случайным образом из пула из 25 000, а код ревизии представляет собой случайное целое число (1-9). После создания источника данных DGV (DataTable) из пары ProductCode-Revision создается LookUp. Это делается один раз и только один раз:

' form level declaration 
Private PRCodes As ILookup(Of String, Int32) 

' go thru table 
' group by the product code 
' create an anon Name-Value object for each, 
'  storing the code and highest rev number 
' convert result to a LookUp 
PRCodes = dtSample.AsEnumerable. 
    GroupBy(Function(g) g.Item("ProductCode"), 
      Function(key, values) New With {.Name = key.ToString(), .Value = values. 
            Max(Of Int32)(Function(j) j.Field(Of Int32)("RevCode")) 
                 }). 
    ToLookup(Of String, Int32)(Function(k) k.Name, Function(v) v.Value) 

Прошедшее время с помощью секундомера: 81 миллисекунды создать коллекцию 23731 предметов. В коде используется анонимный тип для хранения кода Max Revision для каждого кода продукта. Можно также использовать конкретный класс. Если вы беспокоитесь о смешанном корпусе, используйте .ToLowerInvariant() при создании LookUp (не ToUpper - см. What's Wrong With Turkey?), а затем позже, когда вы просматриваете максимальный оборот.

Тогда вместо зацикливания через ряды DGV использовать RowPrePaint события:

If e.RowIndex = -1 Then Return 
If dgv1.Rows(e.RowIndex).IsNewRow Then Return 

' .ToLowerInvariant() if the casing can vary row to row 
Dim pc = dgv1.Rows(e.RowIndex).Cells("ProductCode").Value.ToString() 
Dim rv = Convert.ToInt32(dgv1.Rows(e.RowIndex).Cells("RevCode").Value) 

Dim item = PRCodes(pc)(0) 
If item > rv Then 
    dgv1.Rows(e.RowIndex).DefaultCellStyle.BackColor = Color.MistyRose 
End If 

Примечание

  • Это занимает некоторое время, чтобы создать DataSource, но 75000 строк много, чтобы бросить у пользователя
  • Время создания LookUp минимально - едва измеримо
  • Нет никакого заметного ожидания при их отображении, потому что для этого типа создается LookUp, б) строки производятся по мере необходимости, когда они отображаются. Строка № 19,999 никогда не может быть обработана, если пользователь никогда не прокручивает ее далеко.
  • Это все, что нужно, чтобы просто покрасить ряд.Если вам необходимо сохранить текущее/неточное состояние для каждой строки, добавьте булевский столбец в DataTable и зациклитесь на этом. Столбец может быть невидим, если скрыть его от пользователя.
    • Случайные данные приводят к 47 000 устаревшим RevCodes. Обработка 75k строк в DataTable для установки флага принимает 591 миллисекунды. Вы хотите сделать это до, вы установите DataTable как DataSource, чтобы предотвратить изменения данных, приводящих к различным событиям в элементе управления.

В общем, время сбора флага макс RevCode и даже пометить УСТАРЕЛИ строки является тривиальной добавкой к созданию источника данных.

Результат:

enter image description here

Представление данных сортируются по ProductCode, так что окраска нижнего RevCodes очевидна.

Мы, конечно же, не можем просмотреть все детали и ограничения системы из небольшого фрагмента - даже для типов данных и исходного источника данных мы догадываемся. Однако это должно помочь в улучшении методов поиска и концепции работы с данными, а не в представлении пользователя.

Одна вещь - код пересмотра - ваш обрабатывает их как строку. Если это буквенно-цифровое выражение, оно может не совпадать правильно - «9» сортирует/сравнивает выше, чем «834» или «1JW».

Смотрите также:
Lookup(Of TKey, TElement) Class
Anonymous Types

0

Решение было частично вызвано @Plutonix.

Sub highlightOutdatedParts() 
    If resultsGrid.ColumnCount = 0 Or resultsGrid.RowCount = 0 Then Exit Sub 
    Dim stopwatch As New Stopwatch 
    stopwatch.Start() 
    resultsGrid.DataSource.DefaultView.Sort = "PartNumber ASC, Rev DESC" 
    resultsGrid.Update() 
    'HIGHLIGHT DATAGRIDVIEW ROWS WHERE PART NUMBERS ARE OUT OF DATE 
    Dim irow As Long = 0 
    Do While irow <= resultsGrid.RowCount - 2 
     ' if that part with that Rev does not exist in the list, it must be out of date 
     Dim utdPN As String = resultsGrid.Rows(irow).Cells(4).Value.ToString().ToUpper() 
     Dim utdRev As String = resultsGrid.Rows(irow).Cells(6).Value.ToString().ToUpper() 
     Dim iirow As Long = irow + 1 
     'If iirow > resultsGrid.RowCount - 1 Then Exit Do 
     Dim activePN As String = Nothing 
     Dim activeRev As String = Nothing 
     Try 
      activePN = resultsGrid.Rows(iirow).Cells(4).Value.ToString().ToUpper() 
      activeRev = resultsGrid.Rows(iirow).Cells(6).Value.ToString().ToUpper() 
     Catch ex As NullReferenceException 
     End Try 
     Do While activePN = utdPN 
      If iirow > resultsGrid.RowCount - 1 Then Exit Do 
      If activeRev <> utdRev Then 
       resultsGrid.Rows(iirow).DefaultCellStyle.BackColor = Color.Chocolate 
      End If 
      iirow += 1 
      Try 
       activePN = resultsGrid.Rows(iirow).Cells(4).Value.ToString().ToUpper() 
       activeRev = resultsGrid.Rows(iirow).Cells(6).Value.ToString().ToUpper() 
      Catch nre As NullReferenceException 
      Catch aoore As ArgumentOutOfRangeException 
      End Try 
     Loop 
     irow = iirow 
    Loop 
    resultsGrid.Select() 
    stopwatch.Stop() 
    If Not BackgroundWorker1.IsBusy() Then 
     timertextbox.Text = stopwatch.Elapsed.TotalSeconds.ToString & " secs" 
     resultcounttextbox.Text = resultsGrid.RowCount - 1 & " results" 
    End If 
    MessageBox.Show("Highlighting completed successfully.") 
End Sub 
Смежные вопросы