继承的颜色支持ComboBox多次填充项目

时间:2012-06-27 22:59:54

标签: .net vb.net winforms combobox visual-studio-2008-sp1

下面是继承的ComboBox的代码。问题是ComboBox被多次填充(PopulateComboBox())。

编辑:我接受了 Amit Mittal 的建议(在下面找到他的答案)并实施了ISupportInitialize。现在PopulateComboBox()仅在运行时调用,就像它应该的那样。

通过此实现,项目应在运行时填充,并在退出时销毁。但是,设计者本身在创建运行时创建这些值,而不是在运行时销毁。

是否有优雅的解决方案来实现此代码?

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

3 个答案:

答案 0 :(得分:4)

问题是从构造函数调用PopulateComboBox。

当您在设计器表面设计器上放置组合框时,会在内部调用其构造函数以获取其属性的默认值(包括Item属性)。然后,后面的代码调用构造函数,然后显式指定设计时属性(在您的情况下似乎只是'默认'值)。因此重复了项目。

初始化自定义控件的正确方法是实现ISupportInitialize接口。

在设计师的代码应用了所有设计时属性之后,将会调用一次EndInit(如果您没有使用设计器,那么您有责任调用它)。

所以经验法则是

  1. 对于默认值,请在构造函数
  2. 中指定
  3. 要'初始化',请使用ISupportInitialize。
  4. 例如,除了从中调用PopulateComboBox之外,您还可以使用它将DrawMode属性强制为OwnerDrawFixed,因为这对于您继承的控件至关重要,并且您不希望让控件的用户在设计中指定任何其他内容。时间(虽然这不会完全阻止设计时间'规范'的不同值,但无论在运行时如何,您总是会强制使用正确的值。)

    注意:

    1. 实现此接口后您可能需要重新构建项目,然后再次对设计器表面上的继承控件执行“拖放”操作。您可以检查自动创建的设计器代码,以验证是否确实正在调用EndInit。
    2. 如前所述,设计人员使用ISupportInitialize。如果您不使用设计师,则您有责任调用其方法。

答案 1 :(得分:0)

除非您计划稍后动态更改值,否则它应该在Sub New中。

另外,我不知道你为什么需要清除数组。 GC会处理它。

答案 2 :(得分:0)

在查看您的问题时,我原本以为使用DesignMode属性会阻止在将控件添加到表单时创建ItemArray ......但它不起作用。

然后我将另一个Visual Studio实例附加到项目中,并注意到DesignMode一直设置为false。我无法找到另一个独特的属性,可以让你的控件在添加到表单时不会创建ItemArray(这是导致第一次加载数据的原因)。

您尝试做的事情是独一无二的,因为您正在将数据源构建到Control中并在每次创建时运行它,而普通的ComboBox只是从属性或附加的数据源加载到数组中。

我目前唯一的建议是将您的PopulateComboBox方法从Control的构造函数中取出,然后在父Form的构造函数中调用它,或者动态创建它并将其添加到您的表单中。

即。

Public Class Form1
    Public Sub New()
        InitializeComponent()
        ComboBoxExColors1.PopulateComboBox()
    End Sub
End Class