Imports System.ComponentModel
Imports System.Globalization
Imports System.Drawing.Design

Namespace DynamicObject
    <Serializable()> _
    Public Class PropertyGridOption
        Public Name As String
        Public Value As Object

        Public Sub New(ByVal name As String, ByVal value As Object)
            Me.Name = name
            Me.Value = value
        End Sub

        Public Overrides Function ToString() As String
            Return Name
        End Function
    End Class

    Public Class DefaultValuesConverter
        Inherits TypeConverter

        Public Overloads Overrides Function GetStandardValuesSupported(ByVal context As ITypeDescriptorContext) As Boolean
            Return True
        End Function

        Public Overloads Overrides Function GetStandardValues(ByVal context As ITypeDescriptorContext) As StandardValuesCollection
            Return New StandardValuesCollection(CType(context.PropertyDescriptor, _
                GridPropertyDescriptor).GridProperty.DefaultValues)
        End Function

        Public Overloads Overrides Function CanConvertFrom(ByVal context As ITypeDescriptorContext, ByVal sourceType As Type) As Boolean
            If sourceType Is GetType(String) Then
                Return True
            End If

            Return MyBase.CanConvertFrom(context, sourceType)
        End Function

        Public Overloads Overrides Function ConvertFrom(ByVal context As ITypeDescriptorContext, ByVal culture As CultureInfo, ByVal value As Object) As Object
            If TypeOf value Is String Then
                Dim gp As GridProperty = CType(context.PropertyDescriptor, GridPropertyDescriptor).GridProperty
                Dim sourceValue As String = DirectCast(value, String)

                For Each i As Object In gp.DefaultValues
                    If i.ToString = sourceValue Then
                        Return i
                    End If
                Next

                If gp.Value.GetType Is GetType(String) Then
                    Return sourceValue
                End If

                Dim tc As TypeConverter = TypeDescriptor.GetConverter(gp.Value)

                If Not tc Is Nothing Then
                    If tc.CanConvertFrom(GetType(String)) Then
                        Return tc.ConvertFrom(sourceValue)
                    End If
                End If

                Return gp.Value
            End If

            Return MyBase.ConvertFrom(context, culture, value)
        End Function

        Public Overloads Overrides Function CanConvertTo(ByVal context As ITypeDescriptorContext, ByVal destinationType As Type) As Boolean
            If destinationType Is GetType(String) Then
                Return True
            End If

            Return MyBase.CanConvertTo(context, destinationType)
        End Function

        Public Overloads Overrides Function ConvertTo(ByVal context As ITypeDescriptorContext, ByVal culture As CultureInfo, ByVal value As Object, ByVal destinationType As Type) As Object
            If destinationType Is GetType(String) Then
                Return value.ToString
            End If

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

    Public Class GridProperty
        Public Name As String
        Public Category As String
        Public Description As String
        Public TypeEditor As Type
        Public TypeConverter As Type
        Public DefaultValues As Object()

        Private ValueValue As Object

        Public Property Value() As Object
            Get
                Return ValueValue
            End Get
            Set(ByVal Value As Object)
                If Value.GetType.IsValueType OrElse TypeOf Value Is String Then
                    ValueValue = Value
                Else
                    ValueValue = ObjectHelp.GetCopy(Value)
                End If
            End Set
        End Property

        Public Sub SetGridOption(ByVal gridOptionValue As Object)
            TypeConverter = GetType(DefaultValuesConverter)

            For Each i As PropertyGridOption In DefaultValues
                If i.Value.Equals(gridOptionValue) Then
                    Value = i
                    Exit Sub
                End If
            Next

            Throw New Exception
        End Sub
    End Class

    Public Class GridTypeDescriptor
        Implements ICustomTypeDescriptor

        Private YesNoConverter As Type

        Public Sub New()
            Items = New List(Of Object)
            YesNoConverter = GetType(YesNoConverter)
        End Sub

        Public Sub New(ByVal yesNoConverter As Type)
            Items = New List(Of Object)
            Me.YesNoConverter = yesNoConverter
        End Sub

        Private ItemsValue As List(Of Object)

        Public Property Items() As List(Of Object)
            Get
                Return ItemsValue
            End Get
            Set(ByVal Value As List(Of Object))
                ItemsValue = Value
            End Set
        End Property

#Region "ICustomTypeDescriptor"
        Public Function GetAttributes() As AttributeCollection Implements ICustomTypeDescriptor.GetAttributes
            Return TypeDescriptor.GetAttributes(Me, True)
        End Function

        Public Function GetClassName() As String Implements ICustomTypeDescriptor.GetClassName
            Return TypeDescriptor.GetClassName(Me, True)
        End Function

        Public Function GetComponentName() As String Implements ICustomTypeDescriptor.GetComponentName
            Return TypeDescriptor.GetComponentName(Me, True)
        End Function

        Public Function GetConverter() As TypeConverter Implements ICustomTypeDescriptor.GetConverter
            Return TypeDescriptor.GetConverter(Me, True)
        End Function

        Public Function GetDefaultEvent() As EventDescriptor Implements ICustomTypeDescriptor.GetDefaultEvent
            Return TypeDescriptor.GetDefaultEvent(Me, True)
        End Function

        Public Function GetDefaultProperty() As PropertyDescriptor Implements ICustomTypeDescriptor.GetDefaultProperty
            Return Nothing
        End Function

        Public Function GetEditor(ByVal editorBaseType As Type) As Object Implements ICustomTypeDescriptor.GetEditor
            Return TypeDescriptor.GetEditor(Me, editorBaseType, True)
        End Function

        Public Function GetEvents() As EventDescriptorCollection Implements ICustomTypeDescriptor.GetEvents
            Return TypeDescriptor.GetEvents(Me, True)
        End Function

        Public Function GetEvents(ByVal attributes() As Attribute) As EventDescriptorCollection Implements ICustomTypeDescriptor.GetEvents
            Return TypeDescriptor.GetEvents(Me, attributes, True)
        End Function

        Public Function GetProperties() As PropertyDescriptorCollection Implements ICustomTypeDescriptor.GetProperties
            Return CType(Me, ICustomTypeDescriptor).GetProperties(New Attribute(0) {})
        End Function

        Public Function GetProperties(ByVal attributes() As Attribute) As PropertyDescriptorCollection Implements ICustomTypeDescriptor.GetProperties
            Dim p As New ArrayList

            For Each i As DictionaryEntry In Items
                Dim a As New ArrayList
                Dim gp As GridProperty = CType(i.Value, GridProperty)

                If Not gp.Category Is Nothing AndAlso Not gp.Category = "" Then
                    a.Add(New CategoryAttribute(gp.Category))
                End If

                If Not gp.Description Is Nothing AndAlso Not gp.Description = "" Then
                    a.Add(New DescriptionAttribute(gp.Description))
                End If

                If Not gp.TypeEditor Is Nothing Then
                    a.Add(New EditorAttribute(gp.TypeEditor, GetType(UITypeEditor)))
                End If

                If Not gp.TypeConverter Is Nothing Then
                    a.Add(New TypeConverterAttribute(gp.TypeConverter))
                Else
                    If gp.Value.GetType Is GetType(Boolean) Then
                        a.Add(New TypeConverterAttribute(YesNoConverter))
                    End If
                End If

                Dim d As New GridPropertyDescriptor(gp.Name, gp, _
                    CType(a.ToArray(GetType(Attribute)), Attribute()))

                p.Add(d)
            Next

            Return New PropertyDescriptorCollection(CType(p.ToArray(GetType( _
                PropertyDescriptor)), PropertyDescriptor()))
        End Function

        Public Function GetPropertyOwner(ByVal pd As PropertyDescriptor) As Object Implements ICustomTypeDescriptor.GetPropertyOwner
            Return Me
        End Function
#End Region

        Public Sub Add(ByVal gp As GridProperty)
            Items.Add(New DictionaryEntry(gp.Name, gp))
        End Sub

        Public Function GetValue(ByVal name As String) As Object
            For Each i As DictionaryEntry In Items
                If CStr(i.Key) = name Then
                    Return CType(i.Value, GridProperty).Value
                End If
            Next

            Return Nothing
        End Function

        Public Function GetString(ByVal name As String) As String
            Return DirectCast(GetValue(name), String)
        End Function

        Public Function GetBool(ByVal name As String) As Boolean
            Return DirectCast(GetValue(name), Boolean)
        End Function

        Public Function GetInt(ByVal name As String) As Integer
            Return DirectCast(GetValue(name), Integer)
        End Function

        Public Function GetGridOption(ByVal name As String) As PropertyGridOption
            Return CType(GetValue(name), PropertyGridOption)
        End Function
    End Class

    Public Class GridPropertyDescriptor
        Inherits PropertyDescriptor

        Public GridProperty As GridProperty

        Public Sub New(ByVal name As String, ByVal item As GridProperty, ByVal attributes() As Attribute)
            MyBase.New(name, attributes)

            GridProperty = item
        End Sub

        Public Overrides ReadOnly Property ComponentType() As Type
            Get
                Return GridProperty.GetType()
            End Get
        End Property

        Public Overrides ReadOnly Property IsReadOnly() As Boolean
            Get
                Return Attributes.Matches(ReadOnlyAttribute.Yes)
            End Get
        End Property

        Public Overrides ReadOnly Property PropertyType() As Type
            Get
                Return GridProperty.Value.GetType()
            End Get
        End Property

        Public Overrides Function CanResetValue(ByVal component As Object) As Boolean
            Return False
        End Function

        Public Overrides Function GetValue(ByVal component As Object) As Object
            Return GridProperty.Value
        End Function

        Public Overrides Sub ResetValue(ByVal component As Object)
        End Sub

        Public Overrides Sub SetValue(ByVal component As Object, ByVal value As Object)
            GridProperty.Value = value
        End Sub

        Public Overrides Function ShouldSerializeValue(ByVal component As Object) As Boolean
            Return False
        End Function
    End Class

    Public Class YesNoConverter
        Inherits TypeConverter

        Protected Yes As String = "Yes"
        Protected No As String = "No"

        Public Overloads Overrides Function GetStandardValuesSupported(ByVal context As ITypeDescriptorContext) As Boolean
            Return True
        End Function

        Public Overloads Overrides Function GetStandardValues(ByVal context As ITypeDescriptorContext) As StandardValuesCollection
            Return New StandardValuesCollection(New Boolean() {True, False})
        End Function

        Public Overloads Overrides Function CanConvertFrom(ByVal context As ITypeDescriptorContext, ByVal sourceType As Type) As Boolean
            If sourceType Is GetType(String) Then
                Return True
            End If

            Return MyBase.CanConvertFrom(context, sourceType)
        End Function

        Public Overloads Overrides Function ConvertFrom(ByVal context As ITypeDescriptorContext, ByVal culture As CultureInfo, ByVal value As Object) As Object
            If TypeOf value Is String Then
                If DirectCast(value, String) = Yes Then
                    Return True
                Else
                    Return False
                End If
            End If

            Return MyBase.ConvertFrom(context, culture, value)
        End Function

        Public Overloads Overrides Function CanConvertTo(ByVal context As ITypeDescriptorContext, ByVal destinationType As Type) As Boolean
            If destinationType Is GetType(String) Then
                Return True
            End If

            Return MyBase.CanConvertTo(context, destinationType)
        End Function

        Public Overloads Overrides Function ConvertTo(ByVal context As ITypeDescriptorContext, ByVal culture As CultureInfo, ByVal value As Object, ByVal destinationType As Type) As Object
            If destinationType Is GetType(String) Then
                If DirectCast(value, Boolean) Then
                    Return Yes
                Else
                    Return No
                End If
            End If

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

    Public Class NonDisplayConverter
        Inherits TypeConverter

        Public Overloads Overrides Function ConvertTo(ByVal context As ITypeDescriptorContext, ByVal culture As CultureInfo, ByVal value As Object, ByVal destinationType As Type) As Object
            If destinationType Is GetType(String) Then
                Return "<...>"
            End If

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