LEP LEP - 3 months ago 102x
Vb.net Question

SortCompare Event not Firing for DataGridView Dynamically Added to TabControl

Use of the datatable as a source of data for the datagridview works very nicely, however, I can't get the Dgv_SortCompare event to be fired when a column is being sorted. The main issue is that numeric values in (purely) numeric columns columns in the dgv values are being sorted as text, where e.g. 1211.6 is smaller than 89.7

In Button1:

Dim datagridview1 As New DataGridView
datagridview1.AutoSize = True
DoubleBuffered(datagridview1, True)

Dim dgvColumnHeaderStyle As New DataGridViewCellStyle()
dgvColumnHeaderStyle.Alignment = DataGridViewContentAlignment.MiddleCenter
datagridview1.ColumnHeadersDefaultCellStyle = dgvColumnHeaderStyle
datagridview1.AllowUserToAddRows = False
datagridview1.ScrollBars = ScrollBars.Both
datagridview1.Dock = DockStyle.Fill

datagridview1.VirtualMode = False

Dim dgv As New DGVCREATE(datagridview1, dataarray, columnheaders, rowheaders, InputFeatureNames, InputObjectNames)

TabControl2.SelectedTab = TabControl2.TabPages.Item(0)
AddHandler() datagridview1.SortCompare, AddressOf dgv_SortCompare
AddHandler CType(TabControl2.TabPages(0).Controls(0), DataGridView).SortCompare, AddressOf Me.dgv_SortCompare

Public Class DGVCREATE

Sub New(ByRef dgv As DataGridView, ByVal dataarray(,) As Object, ByVal columnheaders() As String, ByVal rowheaders() As String, ByVal FieldNames() As String, ByVal RowNames() As String)

dgv.RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.DisableResizing

' Create the output table.
GetResultsTable(rxdataarray, columnheaders, rowheaders, FieldNames, RowNames)

'new trial code
dgv.AutoGenerateColumns = False

dgv.DataSource = Form1.MainDataTable
dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill

'Set Column FillWeight in very large DGVs in order to prevent exception related to “Sum of FillWeight exceeds 65000”

For i As Integer = 0 To Form1.MainDataTable.Columns.Count - 1
Dim Column As New DataGridViewTextBoxColumn
Column.Name = Form1.MainDataTable.Columns(i).ColumnName
Column.DataPropertyName = Form1.MainDataTable.Columns(i).ColumnName
Column.HeaderText = Form1.MainDataTable.Columns(i).ColumnName
Column.FillWeight = 20
Column.MinimumWidth = 20
Next i

For i As Integer = 0 To dgv.Columns.Count - 1
If InputFeatureType(i + 1) = 1 Then
dgv.Columns(i).ValueType = GetType(Int64)
dgv.Columns(i).SortMode = DataGridViewColumnSortMode.Automatic
End If
If InputFeatureType(i + 1) = 2 Then
dgv.Columns(i).ValueType = GetType(Double)
dgv.Columns(i).SortMode = DataGridViewColumnSortMode.Automatic
End If
If InputFeatureType(i + 1) = 3 Then
dgv.Columns(i).ValueType = GetType(Double)
dgv.Columns(i).SortMode = DataGridViewColumnSortMode.Automatic
End If
If InputFeatureType(i + 1) = 4 Then
dgv.Columns(i).ValueType = GetType(String)
dgv.Columns(i).SortMode = DataGridViewColumnSortMode.Automatic
End If
Next i

dgv.AutoSize = True
End Sub

Public Sub GetResultsTable(ByVal dataarray(,) As Object, ByVal columnheaders() As String, ByVal rowheaders() As String, ByVal fieldnames() As String, ByVal rownames() As String)
' Loop through all process names.
For j As Integer = 0 To UBound(columnheaders) - 1
' The current process name.
' Add the program name to our columns.
Form1.MainDataTable.Columns.Add(fieldnames(j + 1))
'Form1.MainDataTable.Columns.Add(fieldnames(j + 1))
' Keep adding rows until we have enough.
Do While Form1.MainDataTable.Rows.Count < UBound(rowheaders)
' Add each item to the cells in the column.
For i As Integer = 0 To UBound(rowheaders) - 1
Form1.MainDataTable.Rows(i)(j) = dataarray(i, j)
Next i
Next j
End Sub
End Class

Any suggestions?


You have a lot of unneeded code there. First, you want to avoid boxing your data As Object because it prevents things like the DGV from seeing what type it is. A DataTable is perfectly capable of storing the typed data. If you have to, you could create a class to act like a DTO:

Public Class DataItem
    Public Property Able As Int64
    Public Property Baker As Double
    Public Property Charlie As Double
    Public Property Delta As String
End Class

Since a DataTable can hold multiple types, use it in place of DataArray and avoid the data hopscotching its way there:

Private dtData As DataTable
' create it somewhere
dtData = New DataTable
dtData.Columns.Add("Able", GetType(Int64))
dtData.Columns.Add("Baker", GetType(Double))
dtData.Columns.Add("Chanrlie", GetType(Double))   ' aka Charlie
dtData.Columns.Add("Delta", GetType(String))

Fill it with data:

For n As Int32 = 0 To 999
    dr = dtData.NewRow

    dr(0) = RNG.Next()        ' random value
    dr(1) = RNG.NextDouble()  ' random 0.00 to 1.0
    dr(2) = RNG.Next(-19, 20) + RNG.NextDouble()  ' -19.xx to +19.xx
    dr(3) = RD.GetNames(2, 35)  ' 2 random names
    ' add new row:

When it is filled, let the DGV create the columns for you - it will read the data types etc from the source:

dgv1.DataSource = dtData
For Each dc As DataGridViewColumn In dgv1.Columns

The DGV sees the types and will sort any of them correctly:


enter image description here