0

下面是继承的 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
4

3 回答 3

4

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

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

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

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

所以经验法则是

  1. 对于默认值,在构造函数中指定
  2. 要“初始化”,请使用 ISupportInitialize。

例如,除了从中调用 PopulateComboBox 之外,您还可以使用它将 DrawMode 属性强制为 OwnerDrawFixed,因为这对于您继承的控件至关重要,并且您不希望让控件的用户在设计时指定任何其他内容(尽管这不会完全阻止不同值的设计时“规范”,但不管在运行时如何,您总是会强制使用正确的值。)

笔记:

  1. 实现此接口后,您可能需要重新构建项目,然后再次在设计器表面上对继承的控件进行“拖放”。您可以检查自动创建的设计器代码以验证 EndInit 确实被调用。
  2. 同样如前所述,设计人员使用 ISupportInitialize。如果您不使用设计器,则调用其方法是您的责任。
于 2012-06-28T09:50:16.410 回答
0

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

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

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

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

IE

Public Class Form1
    Public Sub New()
        InitializeComponent()
        ComboBoxExColors1.PopulateComboBox()
    End Sub
End Class
于 2012-06-28T06:09:11.460 回答
0

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

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

于 2012-06-27T23:32:37.030 回答