Question Implement Column/Cell/EditingControl derived classes, host custom ComboBox control

VBobCat

Well-known member
Joined
Sep 6, 2011
Messages
137
Location
S?o Paulo, Brazil
Programming Experience
3-5
I have this custom Control class, derived from ComboBox. Its purpose is to provide a standard behavior for controls whose list of items will be populated depending on what user types in it as search criteria.
Imports System.ComponentModel
Public Class SearchBox
    Inherits ComboBox

    Private hasTextChanged As Boolean

    Private _queryInterval As Integer = 3000
    Public Property QueryInterval As Integer
        Get
            Return _queryInterval
        End Get
        Set(value As Integer)
            _queryInterval = value
            If _myTimer IsNot Nothing Then _myTimer.Interval = value
        End Set
    End Property

    Private WithEvents _myTimer As Timer
    Private ReadOnly Property MyTimer As Timer
        Get
            If _myTimer Is Nothing Then
                _myTimer = New Timer() With {.Enabled = False, .Interval = QueryInterval}
            End If
            Return _myTimer
        End Get
    End Property

    Private WithEvents _myPictureBox As PictureBox
    Private ReadOnly Property MyPictureBox As PictureBox
        Get
            If _myPictureBox Is Nothing Then
                _myPictureBox = New PictureBox() With {
                    .BackColor = Color.Transparent,
                    .BorderStyle = BorderStyle.None,
                    .Parent = Me,
                    .Height = Height - 2,
                    .Width = Height - 2,
                    .Top = 1,
                    .Left = Width - Height,
                    .Visible = False,
                    .SizeMode = PictureBoxSizeMode.CenterImage}
                _myPictureBox.Image = Image.FromStream(
                    New IO.MemoryStream(
                        Convert.FromBase64String(
                            "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")))
            End If
            Return _myPictureBox
        End Get
    End Property

    Friend Property WaitingIcon As Boolean
        Get
            Return MyPictureBox.Visible
        End Get
        Set(value As Boolean)
            MyPictureBox.Visible = value
        End Set
    End Property

    Public Shadows Property DataSource As DataTable
        Get
            Return MyBase.DataSource
        End Get
        Set(value As DataTable)
            MyBase.DataSource = value
        End Set
    End Property

    Public Shadows Property SelectedItem As DataRowView
        Get
            Return MyBase.SelectedItem
        End Get
        Set(value As DataRowView)
            MyBase.SelectedItem = value
        End Set
    End Property

    Private Sub _myTimer_Tick(sender As Object, e As EventArgs) Handles _myTimer.Tick
        MyTimer.Stop()
        SendKeys.Send("{TAB}")
    End Sub

    Protected Overrides Sub OnKeyDown(e As KeyEventArgs)
        MyTimer.Stop()
        If e.KeyValue = 13 Then e.SuppressKeyPress = True
        MyBase.OnKeyDown(e)
    End Sub

    Protected Overrides Sub OnKeyUp(e As KeyEventArgs)
        If e.KeyValue = 13 Then e.SuppressKeyPress = True
        MyBase.OnKeyUp(e)
        If e.KeyValue = 13 Then
            SendKeys.Send("{TAB}")
        ElseIf hasTextChanged
            hasTextChanged = False
            MyTimer.Stop()
            MyTimer.Start()
        End If
    End Sub

    Protected Overrides Sub OnTextChanged(e As EventArgs)
        hasTextChanged = True
        MyBase.OnTextChanged(e)
    End Sub

    Protected WithEvents BGW As New BackgroundWorker
    Private work As Action, whenDone As Action
    Protected Sub DoAction(ByVal work As Action, whendone As Action)
        Me.work = work
        Me.whenDone = whendone
        BGW.RunWorkerAsync()
    End Sub
    Private Sub BGW_DoWork(sender As Object, e As DoWorkEventArgs) Handles BGW.DoWork
        Invoke(Sub() WaitingIcon = True)
        If work IsNot Nothing Then work.Invoke
    End Sub
    Private Sub BGW_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BGW.RunWorkerCompleted
        Invoke(Sub() WaitingIcon = False)
        If whenDone IsNot Nothing Then BeginInvoke(whenDone)
    End Sub
End Class


These controls are working as expected, but now I would like my DataGridViews to host cells that behaved just like these. I tried to follow an example I found on web and wrote those classes below. However, it never worked. It raises the same exception in DataGridView's standard erro dialog, once when it is loaded and again repeats forever whenever I try to click it: System.FormatException: The property FormattedValueType of a cell cannot be null.
    Public Class SearchColumn
        Inherits DataGridViewColumn
        Public Sub New()
            MyBase.New(New SearchCell())
        End Sub
        Public Overrides Property CellTemplate() As DataGridViewCell
            Get
                Return MyBase.CellTemplate
            End Get
            Set(value As DataGridViewCell)
                If (value IsNot Nothing) AndAlso
                    Not value.GetType().IsAssignableFrom(GetType(SearchCell)) _
                    Then
                    Throw New InvalidCastException("Must be a SearchCell")
                End If
                MyBase.CellTemplate = value
            End Set
        End Property
    End Class

    Public Class SearchCell
        Inherits DataGridViewCell
        Public Overrides Sub InitializeEditingControl(ByVal rowIndex As Integer,
            ByVal initialFormattedValue As Object,
            ByVal dataGridViewCellStyle As DataGridViewCellStyle)

            ' Set the value of the editing control to the current cell value.
            MyBase.InitializeEditingControl(rowIndex, initialFormattedValue,
                dataGridViewCellStyle)

            Dim ctl As SearchEditingControl =
                CType(DataGridView.EditingControl, SearchEditingControl)

            ' Use the default row value when Value property is null.
            If (Me.Value Is Nothing) Then
                ctl.Text = CStr(Me.DefaultNewRowValue).Assert
            Else
                ctl.Text = CStr(Me.Value).Assert
            End If
        End Sub
    End Class

    Class SearchEditingControl
        Inherits PessBox
        Implements IDataGridViewEditingControl

        Private dataGridViewControl As DataGridView
        Private valueIsChanged As Boolean = False
        Private rowIndexNum As Integer

        Public Property EditingControlDataGridView As DataGridView Implements IDataGridViewEditingControl.EditingControlDataGridView
            Get
                Return dataGridViewControl
            End Get
            Set(value As DataGridView)
                dataGridViewControl = value
            End Set
        End Property

        Public Property EditingControlFormattedValue As Object Implements IDataGridViewEditingControl.EditingControlFormattedValue
            Get
                Return Me.Text.Assert
            End Get
            Set(value As Object)
                Me.Text = CStr(value).Assert
            End Set
        End Property

        Public Property EditingControlRowIndex As Integer Implements IDataGridViewEditingControl.EditingControlRowIndex
            Get
                Return rowIndexNum
            End Get
            Set(ByVal value As Integer)
                rowIndexNum = value
            End Set
        End Property

        Public Property EditingControlValueChanged As Boolean Implements IDataGridViewEditingControl.EditingControlValueChanged
            Get
                Return valueIsChanged
            End Get
            Set(value As Boolean)
                valueIsChanged = value
            End Set
        End Property

        Public ReadOnly Property EditingPanelCursor As Cursor Implements IDataGridViewEditingControl.EditingPanelCursor
            Get
                Return MyBase.Cursor
            End Get
        End Property

        Public ReadOnly Property RepositionEditingControlOnValueChange As Boolean Implements IDataGridViewEditingControl.RepositionEditingControlOnValueChange
            Get
                Return False
            End Get
        End Property

        Public Sub ApplyCellStyleToEditingControl(dataGridViewCellStyle As DataGridViewCellStyle) Implements IDataGridViewEditingControl.ApplyCellStyleToEditingControl
            Me.Font = dataGridViewCellStyle.Font
            Me.ForeColor = dataGridViewCellStyle.ForeColor
            Me.BackColor = dataGridViewCellStyle.BackColor
        End Sub

        Public Sub PrepareEditingControlForEdit(selectAll As Boolean) Implements IDataGridViewEditingControl.PrepareEditingControlForEdit
            ' No preparation needs to be done.
        End Sub

        Public Function EditingControlWantsInputKey(keyData As Keys, dataGridViewWantsInputKey As Boolean) As Boolean Implements IDataGridViewEditingControl.EditingControlWantsInputKey
            Select Case keyData And Keys.KeyCode
                Case Keys.Left, Keys.Up, Keys.Down, Keys.Right, Keys.Home, Keys.End, Keys.PageDown, Keys.PageUp
                    Return True
                Case Else
                    Return Not dataGridViewWantsInputKey
            End Select
        End Function

        Public Function GetEditingControlFormattedValue(context As DataGridViewDataErrorContexts) As Object Implements IDataGridViewEditingControl.GetEditingControlFormattedValue
            Return Me.Text.Assert
        End Function

        Protected Overrides Sub OnTextChanged(e As EventArgs)
            valueIsChanged = True
            Me.EditingControlDataGridView.NotifyCurrentCellDirty(True)
            MyBase.OnTextChanged(e)
        End Sub

    End Class


Any help you guys could give me, or directions for properly implementing this, will be deeply appreciated. Thanks!
 
Back
Top