Этот подход работает для всего, кроме коллекций:
Коллекции отображаются следующим образом:
Поэтому, несмотря на то, что они расширяемы, для них внутри сети свойств мало пользы.
Вот пример того, что я ищу (снимок экрана отсюда):
Связанная статья также содержит некоторый код, который бы сделал это, но требует модификации исходного класса. Между этим и моим предыдущим вопросом я придумал некоторые идеи, но я не очень свободно говорю об использовании пространства имен System.ComponentModel
.
Ниже приведен сниженный тестовый пример (пользовательский класс с одним свойством типа коллекции, который содержит один объект пользовательского типа, который имеет одно свойство строки):
Imports System.ComponentModel
Public Class Form1
Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Me.AssignTypeConverter(Of MyCustomClassCollection, ExpandableObjectConverter)()
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
Dim collection As New MyCustomClassCollection
collection.Add(New MyCustomClass With {.MyCustomProperty = "Hello"})
Dim container As New MyCustomClassCollectionContainer(collection)
Me.PropertyGrid1.SelectedObject = container
End Sub
Private Sub AssignTypeConverter(Of IType, IConverterType)()
System.ComponentModel.TypeDescriptor.AddAttributes(GetType(IType),
New System.ComponentModel.TypeConverterAttribute(GetType(IConverterType)))
End Sub
End Class
Public Class MyCustomClass
Public Property MyCustomProperty As String
End Class
Public Class MyCustomClassCollection : Inherits System.Collections.ObjectModel.Collection(Of MyCustomClass)
End Class
Public Class MyCustomClassCollectionContainer
Dim _items As MyCustomClassCollection
Public ReadOnly Property Items As MyCustomClassCollection
Get
Return _items
End Get
End Property
Sub New(items As MyCustomClassCollection)
_items = items
End Sub
End Class
Предлагаемое решение (псевдокод, не компилируется)
Imports System.ComponentModel
Public Class MyCustomClassTypeDescriptor : Inherits ExpandableObjectConverter
Public Overrides Function GetProperties(context As ITypeDescriptorContext,
value As Object, attributes() As Attribute) _
As PropertyDescriptorCollection
Dim pds As New PropertyDescriptorCollection(Nothing)
Dim lst As IList(Of Object) = DirectCast(value, IList)
For i As Integer = 0 To lst.Count - 1
Dim item As MyCustomClass = DirectCast(lst.Item(i), MyCustomClass)
'compile error - abstract class cannot be instantiated
Dim pd As New PropertyDescriptor(item)
pds.Add(pd)
Next
Return pds
End Function
End Class
Затем примените этот настраиваемый конвертер объектов во время выполнения.
Будет ли это работать так? Что мне не хватает? Любые предложения приветствуются!
Примечание. Вышеупомянутый вариант - VB.NET, но если вы говорите на С#, не стесняйтесь его использовать.
Слишком долго продолжаться в комментариях, но как насчет чего-то вроде этого - настраиваемый ExpandableObjectConverter
который превращает каждый элемент коллекции в свойство (ItemX) и дескриптор настраиваемого свойства, который получает соответствующий элемент.
Public Class MyCollectionTypeDescriptor(Of TColl As Collection(Of TItem), TItem)
Inherits ExpandableObjectConverter
Public Overrides Function GetProperties(context As ITypeDescriptorContext, value As Object, attributes() As Attribute) As PropertyDescriptorCollection
Dim coll = DirectCast(value, TColl)
Dim props(coll.Count - 1) As PropertyDescriptor
For i = 0 To coll.Count - 1
props(i) = New MyCollectionPropertyDescriptor(Of TColl, TItem)("Item" & CStr(i))
Next
Return New PropertyDescriptorCollection(props)
End Function
End Class
Public Class MyCollectionPropertyDescriptor(Of TColl, TItem)
Inherits PropertyDescriptor
Private _index As Integer = 0
Public Sub New(name As String)
MyBase.New(name, Nothing)
Dim indexStr = Regex.Match(name, "\d+$").Value
_index = CInt(indexStr)
End Sub
Public Overrides Function CanResetValue(component As Object) As Boolean
Return False
End Function
Public Overrides ReadOnly Property ComponentType As Type
Get
Return GetType(TColl)
End Get
End Property
Public Overrides Function GetValue(component As Object) As Object
Dim coll = DirectCast(component, Collection(Of TItem))
Return coll(_index)
End Function
Public Overrides ReadOnly Property IsReadOnly As Boolean
Get
Return True
End Get
End Property
Public Overrides ReadOnly Property PropertyType As Type
Get
Return GetType(TItem)
End Get
End Property
Public Overrides Sub ResetValue(component As Object)
End Sub
Public Overrides Sub SetValue(component As Object, value As Object)
End Sub
Public Overrides Function ShouldSerializeValue(component As Object) As Boolean
Return False
End Function
End Class
Вы можете связать все с вашими классами, используя:
Me.AssignTypeConverter(Of MyCustomClass, ExpandableObjectConverter)()
Me.AssignTypeConverter(Of MyCustomClassCollection, MyCollectionTypeDescriptor(Of MyCustomClassCollection, MyCustomClass))()
Это должно перечислить каждый элемент в основной сетке свойств, и каждый элемент будет расширяемым встроенным. Это то, что вы ищете?
As Collection(Of TItem)
-> IList
. В остальном работал отлично, спасибо за идеальный ответ! +1 и принимаю.
System.ComponentModel.TypeDescriptor.AddEditorTable