在属性网格中创建可扩展组?

时间:2014-12-09 12:07:21

标签: c# vb.net winforms visual-studio user-controls

  

SCENARIO

我已对ListBox进行了细分,并且当我的控件已启用已禁用时,我添加了颜色属性ReadOnly 模式:

enter image description here

  

问题

在C#或VB.Net中,我需要做什么才能将属性网格中的属性组织成具有此结构的可扩展组?:

[+] State Enabled

  [+] Selected Item
      · BackColor
      · ForeColor

  [+] Unselected Item
      · BackColor
      · ForeColor

这是一个从Krypton lib用户控件中获取的可视示例,它演示了我想要模仿的内容:

enter image description here

  

更新

我认为在这个网址中解释了所有关于财产网格的必要条件:

http://www.codeproject.com/Articles/2764/Using-PropertyGrid-Part-I

但它专注于使用具有加载事件的表单来执行此操作,我仍然无法理解如何在我的用户控件中实现该示例,因为如果我创建一个子类来自定义像那个例子中的属性网格,然后我无法访问我的控件的基类。

我的代码有一个简单的结构:

Public Class ElektroListBox : Inherits ListBox

    <Category("Appearance")>
    <Description("The BackColor to paint the selected item when the control is enabled.")>
    Public Property StateEnabledItemSelectedBackColor As Color
        Get
            Return Me.stateEnabledItemSelectedBackColor1
        End Get
        Set(ByVal value As Color)
            Me.stateEnabledItemSelectedBackColor1  = value
            Me.Invalidate(invalidateChildren:=False)
        End Set
    End Property

    Private stateEnabledItemSelectedBackColor1 As Color = Color.Red

End Class

3 个答案:

答案 0 :(得分:3)

TypeConverter并不像听起来那么可怕:首先,ListBox属性声明:

Public Class ListBoxEx
    Inherits ListBox

    <Browsable(True), EditorBrowsable(EditorBrowsableState.Always),
    DesignerSerializationVisibility(DesignerSerializationVisibility.Content)>
    Public Property SelectedItemColor As ItemStateColors

    <Browsable(True), EditorBrowsable(EditorBrowsableState.Always),
    DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
    DefaultValue(-1)>
    Public Property UnSelectedItemColor As ItemStateColors

    Public Sub New()
        ' they are Objects, be sure to instance them!
        ' VERY important!
        SelectedItemColor = New ItemStateColors
        UnSelectedItemColor = New ItemStateColors

    End Sub

end class

接下来定义您的ItemStateColors班级:

<TypeConverter(GetType(ItemStateConverter))>
Public Class ItemStateColors

    <Browsable(True), NotifyParentProperty(True),
    EditorBrowsable(EditorBrowsableState.Always), DefaultValue(GetType(Color), "")>
    Public Property EnabledBackColor As Color

    <Browsable(True), NotifyParentProperty(True),
    EditorBrowsable(EditorBrowsableState.Always), DefaultValue(GetType(Color), "")>
    Public Property DisabledBackColor As Color

    Public Sub New()
        ' default values, if any
        EnabledBackColor = SystemColors.Window
        DisabledBackColor = SystemColors.Control
    End Sub

End Class

您的每个ItemState属性都是此类的一个实例。请注意,Type包含TypeConverter属性 - 这提供了“魔力”。我们将提供expando功能并转换内容以在属性网格中显示:

Public Class ItemStateConverter
    Inherits ExpandableObjectConverter

    Public Overrides Function ConvertTo(context As ITypeDescriptorContext,
                               culture As Globalization.CultureInfo,
                               value As Object, destinationType As Type) As Object

        If destinationType Is GetType(String) Then
            Dim item As ItemStateColors = CType(value, ItemStateColors)

            ' ToDo: decide the format of collapsed info            
            Return String.Format("{0}, {1}", item.EnabledBackColor.ToString,
                                 item.DisabledBackColor.ToString)

        End If

        Return MyBase.ConvertTo(context, culture, value, destinationType)
    End Function

End Class

Component方法更容易实现,但TypeConverter允许您控制属性折叠时显示的内容:

enter image description here


TypeConverter 通常执行的功能之一是为设计器序列化/反序列化类型的代码。这里不需要这样,因为Type只是Color,VS / NET知道怎么做。它在这种情况下做的是:
   - 将该属性标记为可扩展
   - 在财产崩溃时提供“摘要”信息

您需要的另一件事是将一种类型嵌入另一种类型(如myControl.StateEnabled.SelectedItem.ForeColor)。通过嵌套它们,您将需要TypeConverter或某些代码解决方案,例如实例引用(我永远无法分辨您的问题的哪些元素是必须的)。 VS只知道钻入第一层,你必须提供TypeConverter来深入挖掘以获取颜色数据。但是,SelectedItemDeselectedItemReadOnlyItem都可以使用相同的TypeConverter

enter image description here

继承自Component的Foo Type也提供空下拉。

另见:

ExpandableObjectConverter
How to: Implement a Type Converter


最后注意事项:您的所有Prop Setters都应该测试传递的值并拒绝Color.Transparent

答案 1 :(得分:1)

[Category]属性是否适用于此?

Example

答案 2 :(得分:1)

您需要做的第一件事是将属性结构化为类。然后,您需要为每个类创建一个自定义type converter,以便它可以序列化。但是,有一种更简单的方法来实现这一目标;继承Component类。这是一个简单的例子。

Public Class UIListBox
    Inherits ListBox

    Public Sub New()
        Me.m_stateDisabled = New ItemLayout(Me)
        Me.m_stateEnabled = New ItemLayout(Me)
        Me.m_stateReadOnly = New ItemLayout(Me)
    End Sub

    <DesignerSerializationVisibility(DesignerSerializationVisibility.Content)>
    Public ReadOnly Property StateDisabled() As ItemLayout
        Get
            Return Me.m_stateDisabled
        End Get
    End Property

    <DesignerSerializationVisibility(DesignerSerializationVisibility.Content)>
    Public ReadOnly Property StateEnabled() As ItemLayout
        Get
            Return Me.m_stateEnabled
        End Get
    End Property

    <DesignerSerializationVisibility(DesignerSerializationVisibility.Content)>
    Public ReadOnly Property StateReadOnly() As ItemLayout
        Get
            Return Me.m_stateReadOnly
        End Get
    End Property

    Friend Sub NotifyStateChanged(source As ItemLayoutColors, propertyName As String)
        Me.Invalidate()
        Debug.WriteLine("UIListBox: State changed.")
    End Sub

    Private m_stateDisabled As ItemLayout
    Private m_stateEnabled As ItemLayout
    Private m_stateReadOnly As ItemLayout

End Class

<ToolboxItem(False)>
Public Class ItemLayout
    Inherits Component

    Public Sub New(listBox As UIListBox)
        Me.m_listBox = listBox
        Me.m_background = New ItemLayoutColors(Me)
        Me.m_foreground = New ItemLayoutColors(Me)
    End Sub

    Friend ReadOnly Property ListBox() As UIListBox
        Get
            Return Me.m_listBox
        End Get
    End Property

    <DesignerSerializationVisibility(DesignerSerializationVisibility.Content)>
    Public ReadOnly Property Background() As ItemLayoutColors
        Get
            Return Me.m_background
        End Get
    End Property

    <DesignerSerializationVisibility(DesignerSerializationVisibility.Content)>
    Public ReadOnly Property Foreground() As ItemLayoutColors
        Get
            Return Me.m_foreground
        End Get
    End Property

    Private m_background As ItemLayoutColors
    Private m_foreground As ItemLayoutColors
    Private m_listBox As UIListBox

End Class

<ToolboxItem(False)>
Public Class ItemLayoutColors
    Inherits Component

    Public Sub New(layout As ItemLayout)
        If (layout Is Nothing) Then Throw New ArgumentNullException("layout")
        Me.m_layout = layout
    End Sub

    Friend ReadOnly Property Layout() As ItemLayout
        Get
            Return Me.m_layout
        End Get
    End Property

    Public Property Selected() As Color
        Get
            Return Me.m_selected
        End Get
        Set(value As Color)
            If (value <> Me.m_selected) Then
                Me.m_selected = value
                Me.Layout.ListBox.NotifyStateChanged(Me, "Selected")
            End If
        End Set
    End Property

    Public Property Unselected() As Color
        Get
            Return Me.m_unselected
        End Get
        Set(value As Color)
            If (value <> Me.m_unselected) Then
                Me.m_unselected = value
                Me.Layout.ListBox.NotifyStateChanged(Me, "Unselected")
            End If
        End Set
    End Property

    Private Function ShouldSerializeSelected() As Boolean
        Return (Me.Selected <> Color.Empty)
    End Function

    Private Function ShouldSerializeUnselected() As Boolean
        Return (Me.Unselected <> Color.Empty)
    End Function

    Private m_selected As Color
    Private m_unselected As Color
    Private m_layout As ItemLayout

End Class

Property Window

设计师档案

Me.UiListBox1.StateDisabled.Background.Selected = System.Drawing.Color.Red