1
\$\begingroup\$

The issue that i'm trying to fix with this code is simple

I have a huge dataset and I have to bind it (as readonly) to a DataGridView

In that dataset I have some column that are key to list and I have to display a custom property from that list

with the DataGridViewComboBoxColumn there is a big issue of unsharing row which cause huge slowdown when disposing of the bindingsource

This is a work in progress (which actually work)

Is there anything missing or any red flag?

Another note, this code must work under net 3.5

DataGridViewColumn:

Option Strict On
Option Explicit On

Imports System.ComponentModel
Imports System.Drawing.Design
Imports System.Reflection

Public Class DataGridViewTypeInfoColumn
    Inherits DataGridViewColumn

    Public Sub New()
        MyBase.New(New DataGridViewTypeInfoCell())
    End Sub

    Private _dataSource As Object
    Private _displayMember As String
    Private _valueMember As String

    Private _bindingDataSource As BindingSource
    Private _bindingDescriptorCollection As PropertyDescriptorCollection
    Private _valueMemberDescriptor As PropertyDescriptor
    Private _displayMemberDescriptor As PropertyDescriptor

    <DefaultValue(""),
     AttributeProvider(GetType(IListSource)),
     RefreshProperties(RefreshProperties.Repaint),
     Category("Data")>
    Public Property DataSource As Object
        Get
            Return _dataSource
        End Get
        Set(value As Object)
            CleanUp()
            _dataSource = value
        End Set
    End Property

    <DefaultValue(""),
     Editor("System.Windows.Forms.Design.DataMemberFieldEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", GetType(UITypeEditor)),
     TypeConverter("System.Windows.Forms.Design.DataMemberFieldConverter, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"),
     Category("Data"),
      Localizable(True)>
    Public Property DisplayMember() As String
        Get
            Return _displayMember
        End Get
        Set(value As String)
            _displayMember = value
        End Set
    End Property

    <DefaultValue(""),
     Editor("System.Windows.Forms.Design.DataMemberFieldEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", GetType(UITypeEditor)),
     TypeConverter("System.Windows.Forms.Design.DataMemberFieldConverter, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"),
     Category("Data")>
    Public Property ValueMember() As String
        Get
            Return _valueMember
        End Get
        Set(value As String)
            _valueMember = value
        End Set
    End Property

    Public Function GetValue(ByVal originalValue As Object) As Object
        Dim returnValue As Object

        Try
            If TypeOf originalValue Is String AndAlso originalValue IsNot Nothing Then
                originalValue = originalValue.ToString.Trim
            End If

            If _dataSource IsNot Nothing Then
                If _bindingDataSource Is Nothing Then
                    _bindingDataSource = DirectCast(_dataSource, BindingSource)
                End If

                If _bindingDescriptorCollection Is Nothing AndAlso _bindingDataSource.Current IsNot Nothing Then
                    _bindingDescriptorCollection = TypeDescriptor.GetProperties(_bindingDataSource.Current.GetType)
                    If _bindingDescriptorCollection IsNot Nothing Then
                        _valueMemberDescriptor = _bindingDescriptorCollection.Find(ValueMember, True)
                        _displayMemberDescriptor = _bindingDescriptorCollection.Find(DisplayMember, True)
                    End If
                End If
            End If

            If _valueMemberDescriptor IsNot Nothing AndAlso _displayMemberDescriptor IsNot Nothing Then
                For Each item In _bindingDataSource.List
                    returnValue = _valueMemberDescriptor.GetValue(item)

                    If originalValue.Equals(returnValue) Then
                        returnValue = _displayMemberDescriptor.GetValue(item)

                        Return returnValue
                    End If
                Next
            End If
        Catch ex As Exception

        End Try

        Return originalValue
    End Function

    Public Overrides Function Clone() As Object
        Dim myClone As DataGridViewTypeInfoColumn = DirectCast(MyBase.Clone, DataGridViewTypeInfoColumn)

        myClone.DataSource = DataSource
        myClone.DisplayMember = DisplayMember
        myClone.ValueMember = ValueMember

        Return myClone
    End Function

    Private Sub CleanUp()
        If _bindingDataSource IsNot Nothing Then
            _bindingDataSource.Dispose()
        End If
        _valueMemberDescriptor = Nothing
        _displayMemberDescriptor = Nothing
        _bindingDescriptorCollection = Nothing
        _bindingDataSource = Nothing
        _dataSource = Nothing
    End Sub

    Protected Overrides Sub Dispose(disposing As Boolean)
        CleanUp()
        MyBase.Dispose(disposing)
    End Sub
End Class

DataGridViewCell:

Option Strict On
Option Explicit On

Imports System.ComponentModel

Public Class DataGridViewTypeInfoCell
    Inherits DataGridViewTextBoxCell

    Protected Overrides Function GetFormattedValue(value As Object, rowIndex As Integer, ByRef cellStyle As DataGridViewCellStyle, valueTypeConverter As TypeConverter, formattedValueTypeConverter As TypeConverter, context As DataGridViewDataErrorContexts) As Object
        Dim formattedValue As Object = DirectCast(Me.OwningColumn, DataGridViewTypeInfoColumn).GetValue(value)

        Return MyBase.GetFormattedValue(formattedValue, rowIndex, cellStyle, valueTypeConverter, formattedValueTypeConverter, context)
    End Function
End Class
\$\endgroup\$
1
  • \$\begingroup\$ If I'm following you (which may be in doubt), it seems like an easy approach would be to use the CellFormatting event to alter the behavior of that one column. That way you wouldn't have to create an entire inherited class. For example, I do this when the database has a "long" for a date, and I want to convert this to a DateTime \$\endgroup\$
    – egray
    Jun 14, 2016 at 23:00

1 Answer 1

1
\$\begingroup\$

Focusing mainly on this

Public Function GetValue(ByVal originalValue As Object) As Object
    Dim returnValue As Object

    Try
        If TypeOf originalValue Is String AndAlso originalValue IsNot Nothing Then
            originalValue = originalValue.ToString.Trim
        End If

        If _dataSource IsNot Nothing Then
            If _bindingDataSource Is Nothing Then
                _bindingDataSource = DirectCast(_dataSource, BindingSource)
            End If

            If _bindingDescriptorCollection Is Nothing AndAlso _bindingDataSource.Current IsNot Nothing Then
                _bindingDescriptorCollection = TypeDescriptor.GetProperties(_bindingDataSource.Current.GetType)
                If _bindingDescriptorCollection IsNot Nothing Then
                    _valueMemberDescriptor = _bindingDescriptorCollection.Find(ValueMember, True)
                    _displayMemberDescriptor = _bindingDescriptorCollection.Find(DisplayMember, True)
                End If
            End If
        End If

        If _valueMemberDescriptor IsNot Nothing AndAlso _displayMemberDescriptor IsNot Nothing Then
            For Each item In _bindingDataSource.List
                returnValue = _valueMemberDescriptor.GetValue(item)

                If originalValue.Equals(returnValue) Then
                    returnValue = _displayMemberDescriptor.GetValue(item)

                    Return returnValue
                End If
            Next
        End If
    Catch ex As Exception

    End Try

    Return originalValue
End Function
  • By reversing the _dataSource IsNot Nothing condition you could return early which will save you some horizontal space.

  • if you set the _bindingDataSource at the DataSource setter this could be removed here.

  • if you think you can safely swallow any exception, you should make it clear with some comments why you are swallowing. Or at least add some logging to it.

  • adding some vertical space (new line) to group related code will improve the readability.

Otherwise your code looks clean, you have Option Explicit On and the used names for method arguments, local variables and the classes are good and are following the NET naming guidelines.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.