Below is code to an inherited ComboBox. The issue is that the ComboBox is being populated (PopulateComboBox()) multiple times.
Edit: I’ve taken the advice of Amit Mittal (find his answer below) and implemented ISupportInitialize. Now PopulateComboBox() is only called at run-time, like it should be.
By this implementation, the items should be populated during run-time, and destroyed on exit. However, the designer itself is storing these values when they’re created run-time, and not destroying after run-time.
Is there an elegant solution to the implementation of this code?
Public Class ComboBoxExColors
Inherits ComboBox
Implements ISupportInitialize
Public Sub New()
MyBase.New()
Me.Size = New Size(146, 23)
Me.DropDownStyle = ComboBoxStyle.DropDownList
Me.MaxDropDownItems = 16
End Sub
Public Sub BeginInit() Implements System.ComponentModel.ISupportInitialize.BeginInit
' Do nothing?
End Sub
Public Sub EndInit() Implements System.ComponentModel.ISupportInitialize.EndInit
Me.DrawMode = DrawMode.OwnerDrawVariable ' fixed or variable?
Me.PopulateComboBox()
End Sub
Public Sub PopulateComboBox()
'Me.Items.Clear() ' rather than forcing items to be cleared, is there a more elegant solution to the implementation of this code, rather than forcing an item clear that shouldn't exist to begin with?
Me.Items.Add("Default")
Me.Items.Add("Custom")
Dim KnownColors() As String = System.Enum.GetNames(GetType(System.Drawing.KnownColor)) ' get all colors
For Each c As String In KnownColors ' add non system colors
If Not Color.FromName(c).IsSystemColor Then
Me.Items.Add(c)
End If
Next c
End Sub
Protected Overrides Sub OnDrawItem(ByVal e As DrawItemEventArgs)
' this draws each item onto the control
If e.Index > -1 Then
Dim item As String = Me.Items(e.Index).ToString
e.DrawBackground()
e.Graphics.DrawString(item, e.Font, SystemBrushes.WindowText, e.Bounds.X, e.Bounds.Y)
e.DrawFocusRectangle()
End If
End Sub
End Class
The problem is PopulateComboBox is being invoked from the constructor.
When you drop your combobox on designer surface designer internally invokes its constructor to get default values of its properties (including Item property). And then, the code behind calls the constructor and after that explicitly specifies the design time properties as well (which in your case seems to be the ‘default’ values only). Hence duplicating of Items.
Proper way to initialize a custom control is by implementing ISupportInitialize interface.
EndInit will be called exactly once after designer’s code has applied all the design time properties (if you are not using a designer then it will be your responsibility to call it).
So rule of thumb is
For instance in addition to calling PopulateComboBox from it, you can also use it to force DrawMode property to OwnerDrawFixed as this is crucial for your inherited control and you would not want to let the user of the control to specify anything else at design time (though this will not exactly prevent design time ‘specification’ of a different value but regardless of that at run time you will have always forced the correct value.)
Note: