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

情景

我已经对ListBox细分,并且在我的控件启用禁用或处于ReadOnly模式时添加了颜色属性:

在此处输入图像描述

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

 [+] State Enabled [+] Selected Item · BackColor · ForeColor [+] Unselected Item · BackColor · ForeColor 

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

在此处输入图像描述

UPDATE

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

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

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

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

 Public Class ElektroListBox : Inherits ListBox   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 

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

 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  Public ReadOnly Property StateDisabled() As ItemLayout Get Return Me.m_stateDisabled End Get End Property  Public ReadOnly Property StateEnabled() As ItemLayout Get Return Me.m_stateEnabled End Get End Property  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  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  Public ReadOnly Property Background() As ItemLayoutColors Get Return Me.m_background End Get End Property  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  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 

物业窗口

设计师档案

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

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

 Public Class ListBoxEx Inherits ListBox  Public Property SelectedItemColor As ItemStateColors  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类:

  Public Class ItemStateColors  Public Property EnabledBackColor As 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属性 – 这提供了“魔术”。 我们将提供expandofunction并翻译内容以在属性网格中显示:

 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允许您控制属性折叠时显示的内容:

在此处输入图像描述


TypeConverter 通常执行的function之一是为设计器序列化/反序列化类型的代码。 这里不需要这样,因为Type只是Color ,VS / NET知道怎么做。 它在这个例子中做的是:
– 将属性标记为可扩展
– 折叠属性时提供“摘要”信息

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

在此处输入图像描述

inheritance自Component的Foo Type也提供空下拉列表。

也可以看看:

ExpandableObjectConverter
如何:实现类型转换器


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

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