【问题标题】:VB.NET - Faster way to update a Large DataTable which is the DataSource for a DataGridView?VB.NET - 更新作为 DataGridView 的数据源的大型数据表的更快方法?
【发布时间】:2018-01-15 11:47:43
【问题描述】:

我只使用一个二维数组,即 x(,) As Object,它基于用户打开的矩形数据文件。 x(,) 的内容主要是数字单精度或双精度实数和整数。因此,数组的维度可以从 x(100,100) 到例如x(100000,1000)。

datagridview 是动态创建的,并通过调用 DGVCREATE 类进行更新。

为了减少datagridview使用的数据源的sizer,datagridview1.scroll有一个AddHandler,这样当用户滚动datagridview时,左上角的单元格行和列就会消失,然后一个尝试将 x(,) 数组中的下 20 列和下 50 行添加到 DataTable 中,假设更新了 datagridview 中显示的数据。但是,这不起作用。

一个问题,如果我用用户输入的整个数组填充 DataTable,如何设置 datagridview1 以便仅将 50 行和 20 列添加到可见范围。如果没有滚动 addhandler 来选择用户在数组中的位置,并且在将整个数组填充到 DataTable 中时,此代码可以正常工作,但是在需要添加的计算更新之后添加列时,更新 DataTable 需要很长时间的更多领域。换句话说,当有例如DataTable中1000行50个字段,另外20列添加到x(,)[即Redim Preserve x(rows,50+20)],然后更新DataTable,添加额外需要很长时间当 DataTable 已经有 1000 行和 50 列时,它有 20 列。

'In a global module:
Public MainDataTable As New DataTable
Public FieldType() As Byte ' Byte array to denote the type of field (object, double, long, string, etc.)


Sub showdgv()
    Dim x(1000, 100) As Object
    Dim columnheaders(100) As String
    Dim rowheaders(1000) As String
    Dim fieldnames(100) As String
    Dim rownames(1000) As String

    For i as Integer = 0 To 999
       For j As Integer = 0 to 99
           x(i, j) = Rnd()
       Next
    Next

    'Dynamically create a datagridview
    Dim datagridview1 As New DataGridView
    Me.Controls.Add(datagridview1)
    datagridview1.AutoSize = True
    datagridview1.AutoResizeRows()
    datagridview1.AutoResizeColumns()
    datagridview1.ClearSelection()
    DoubleBufferedDGV(datagridview1, True)
    datagridview1.AutoResizeRowHeadersWidth(DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders)
    Dim dgvColumnHeaderStyle As New DataGridViewCellStyle()
    dgvColumnHeaderStyle.Alignment = DataGridViewContentAlignment.MiddleCenter
    datagridview1.ColumnHeadersDefaultCellStyle = dgvColumnHeaderStyle
    datagridview1.AllowUserToAddRows = False
    datagridview1.ScrollBars = ScrollBars.Both
    datagridview1.Refresh()
    datagridview1.Dock = DockStyle.Fill
    AddHandler datagridview1.Scroll, AddressOf Me.DGV1_Scroll

    'Instantiate the class object to update the DGV by loading the DataTable with the contant of the array x(,) 
    Dim dgv As New DGVCREATE(datagridview1, x, columnheaders, rowheaders, FieldNames, RowNames)

End Sub


Public Class DGVCREATE
    Dim x(,) As Object
    Dim columnheaders() As String
    Dim rowheaders() As String
    Dim fieldnames() As String
    Dim rownames() As String
    Sub New(ByRef dgv As DataGridView, ByVal x(,) As Object, ByVal columnheaders() As String, ByVal rowheaders() As String, ByVal FieldNames() As String, ByVal RowNames() As String)
        Dim main As Form1 = CType(Application.OpenForms(0), Form1)
        dgv.RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.DisableResizing
        GetResultsTable(x, columnheaders, rowheaders, FieldNames, RowNames)
        'new trial code
        dgv.AutoGenerateColumns = True
        'Application.DoEvents()
        dgv.DataSource = main.MainDataTable
        dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill
        For i As Integer = 0 To main.MainDataTable.Columns.Count - 1
            Dim Column As New DataGridViewTextBoxColumn
            Column.Name = main.MainDataTable.Columns(i).ColumnName
            Column.DataPropertyName = main.MainDataTable.Columns(i).ColumnName
            Column.HeaderText = main.MainDataTable.Columns(i).ColumnName
            Column.FillWeight = 70
            Column.MinimumWidth = 70
            dgv.Columns.Add(Column)
        Next i
        dgv.AutoSize = True
    End Sub

    Public Sub GetResultsTable(ByVal x(,) As Object, ByVal columnheaders() As String, ByVal rowheaders() As String, ByVal fieldnames() As String, ByVal rownames() As String)
        Dim main As Form1 = CType(Application.OpenForms(0), Form1)
        main.MainDataTable.Clear()
        main.MainDataTable.Rows.Clear()
        main.MainDataTable.Columns.Clear()

        ' Loop through all process names.
        For j As Integer = dgvbegincol To dgvbegincol + 30 ' 0 To UBound(columnheaders) - 1
            ' The current process name.
            ' Add the program name to our columns.
            Try
                If FieldType(j + 1) = 0 Then main.MainDataTable.Columns.Add(fieldnames(j + 1), GetType(Object))
                If FieldType(j + 1) = 1 Then main.MainDataTable.Columns.Add(fieldnames(j + 1), GetType(Double))
                If FieldType(j + 1) = 2 Then main.MainDataTable.Columns.Add(fieldnames(j + 1), GetType(Long))
                If FieldType(j + 1) = 3 Then main.MainDataTable.Columns.Add(fieldnames(j + 1), GetType(Long))
                If FieldType(j + 1) = 4 Then main.MainDataTable.Columns.Add(fieldnames(j + 1), GetType(String))
            Catch ex As Exception
                If InStr(ex.Message, "column name") > 0 Then
                    Try
                        If FieldType(j + 1) = 0 Then main.MainDataTable.Columns.Add(fieldnames(j + 1) & "_" & varsuffixvalue.ToString, GetType(Object))
                        If FieldType(j + 1) = 1 Then main.MainDataTable.Columns.Add(fieldnames(j + 1) & "_" & varsuffixvalue.ToString, GetType(Double))
                        If FieldType(j + 1) = 2 Then main.MainDataTable.Columns.Add(fieldnames(j + 1) & "_" & varsuffixvalue.ToString, GetType(Long))
                        If FieldType(j + 1) = 3 Then main.MainDataTable.Columns.Add(fieldnames(j + 1) & "_" & varsuffixvalue.ToString, GetType(Long))
                        If FieldType(j + 1) = 4 Then main.MainDataTable.Columns.Add(fieldnames(j + 1) & "_" & varsuffixvalue.ToString, GetType(String))
                        fieldnames(j + 1) = fieldnames(j + 1) & "_" & varsuffixvalue.ToString
                    Catch ex1 As Exception
                        If InStr(ex1.Message, "column name") > 0 Then
                            varsuffixvalue += 1
                            If FieldType(j + 1) = 0 Then main.MainDataTable.Columns.Add(fieldnames(j + 1) & "_" & varsuffixvalue.ToString, GetType(Object))
                            If FieldType(j + 1) = 1 Then main.MainDataTable.Columns.Add(fieldnames(j + 1) & "_" & varsuffixvalue.ToString, GetType(Double))
                            If FieldType(j + 1) = 2 Then main.MainDataTable.Columns.Add(fieldnames(j + 1) & "_" & varsuffixvalue.ToString, GetType(Long))
                            If FieldType(j + 1) = 3 Then main.MainDataTable.Columns.Add(fieldnames(j + 1) & "_" & varsuffixvalue.ToString, GetType(Long))
                            If FieldType(j + 1) = 4 Then main.MainDataTable.Columns.Add(fieldnames(j + 1) & "_" & varsuffixvalue.ToString, GetType(String))
                            fieldnames(j + 1) = fieldnames(j + 1) & "_" & varsuffixvalue.ToString
                        End If
                    End Try
                End If
            End Try

            Do While main.MainDataTable.Rows.Count < UBound(rowheaders)
                Dim myRow As DataRow
                myRow = main.MainDataTable.NewRow()
                main.MainDataTable.Rows.Add(myRow)
            Loop
            ' Add each item to the cells in the column.
            For i As Integer = dgvbeginrow To dgvbeginrow + 50 '  0 To UBound(rowheaders) - 1
                main.MainDataTable.Rows(i)(j) = If(x(i, j), CObj(DBNull.Value))
            Next i
        Next j
        dgvThreadDone.Set()
    End Sub
End Class

Sub DGV1_Scroll(sender As Object, e As ScrollEventArgs)
    Dim x(,) As Object
    Dim rowheaders() As String, columnheaders() As String
    rowheaders = RowNames.Clone
    columnheaders = FieldNames.Clone

    For Each c In Me.Controls
        If TypeOf (c) Is DataGridView Then
            'Public dgvbeginrow, dgvendrow, dgvbegincol, dgvendcol As Integer
            dgvbeginrow = c.FirstDisplayedCell.RowIndex
            dgvbegincol = c.FirstDisplayedCell.ColumnIndex

            Dim dgv As New DGVCREATE(c, x, columnheaders, rowheaders, FieldNames, RowNames)

        End If
    Next
End Sub

【问题讨论】:

  • 我建议你用DataTable替换你的二维数组。

标签: arrays vb.net datagridview datatable


【解决方案1】:

我认为使用DataTable 填充DataGridView 会获得更好的结果:

Public Class Form1
  'For this example, add a DataGridView and Button under it 
  Sub New()

    ' This call is required by the designer.
    InitializeComponent()

    ' Add any initialization after the InitializeComponent() call.
    Dim dtb As New DataTable()
    'Start with 50 columns and 1000 rows
    For intCol As Integer = 0 To 49
      dtb.Columns.Add("Col" & intCol.ToString("000"))
    Next intCol
    For intRow As Integer = 0 To 999
      Dim drw As DataRow = dtb.NewRow
      For intCol As Integer = 0 To 49
        drw(intCol) = Rnd()
      Next intCol
      dtb.Rows.Add(drw)
    Next intRow
    DataGridView1.DataSource = dtb
    DataGridView1.Anchor = AnchorStyles.Top Or AnchorStyles.Left Or AnchorStyles.Right Or AnchorStyles.Bottom
    Button1.Anchor = AnchorStyles.Left Or AnchorStyles.Bottom
  End Sub

  Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    'Add 20 columns
    Me.Cursor = Cursors.WaitCursor
    Dim dtb As DataTable = DirectCast(DataGridView1.DataSource, DataTable)
    Dim intOldColumnCount As Integer = dtb.Columns.Count
    For intCol As Integer = intOldColumnCount To intOldColumnCount + 20
      dtb.Columns.Add("Col" & intCol.ToString("000"))
    Next intCol
    For intRow As Integer = 0 To dtb.Rows.Count - 1
      Dim drw As DataRow = dtb.Rows(intRow)
      For intCol As Integer = intOldColumnCount To intOldColumnCount + 20
        drw(intCol) = Rnd()
      Next intCol
    Next intRow
    Me.Cursor = Cursors.Default
  End Sub
End Class

【讨论】:

  • 谢谢,我试试代码示例!如果我先更新数组(基本数学,非常快),然后填充数据表,我不是已经在这里做了吗:main.MainDataTable.Rows(i)(j) = If(x(i, j), CObj(DBNull.Value))?我认为这就是如此缓慢的原因。回想一下,在整个 DataTable 被更新后的数组填充,并且加载了 datagridview 之后,我可以非常快速地在整个 datagridview 中移动。但是,重置 DataTable 似乎需要这么长时间(?)
  • 另外,在您的代码示例中,您使用随机数填充 DataTable,而我有一个包含必须复制到 DataTable 的随机数的数组。由于数组的数学运算速度非常快,因此对 DataTable(?)的单元格进行数学运算是没有意义的建议。
  • 在您添加DataGridViewTextBoxColumns 的循环的每次迭代中,dgv 都可能重新绘制。您可以尝试设置.AutoGenerateColumns = False 并在设置.DataSource 之前配置您的DataGridViewTextBoxColumns。我从来没有用DataGridView.SuspendLayout 来抑制重绘,所以我尝试尽可能长时间地延迟设置DataSource
  • 另外,您当前的代码有多慢?我发布的代码大约需要 3-5 秒来绘制,对于这么大的数据集我认为可以接受
  • 通过使用 LoadDataRow(rowarray,True) 将数组中的每一行加载到数据表中,OP 中的代码大大加快了速度,其中 rowarray() 是一个对象数组,其内容等于数组每一行的元素。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-01-28
  • 1970-01-01
  • 1970-01-01
  • 2012-02-09
  • 2013-08-30
  • 2020-03-21
  • 1970-01-01
相关资源
最近更新 更多