Imports System.ComponentModel
Imports System.Reflection

Namespace ComponentModel
    <ToolboxItem(False)> _
        Public Class PropertyControl
        Inherits Control
        Implements IEditor

        Private Definitions As New Hashtable
        Private ItemType As Type
        Private SelectedObjects As Object()
        Private Properties As PropertyDescriptorCollection
        Private Binders As New Hashtable

        Public Event ValueChanged() Implements IEditor.ValueChanged

        Public Sub RaiseValueChanged()
            RaiseEvent ValueChanged()
        End Sub

        Public Sub New(ByVal itemType As Type)
            Me.ItemType = itemType
            Definitions(GetType(Boolean)) = New Definition(GetType(Boolean), GetType(PropertyCheckBox), "FlatStyle=System")
            Definitions(GetType(String)) = New Definition(GetType(String), GetType(LabelTextBox), "")
        End Sub

        Public Sub SetSelectedObjects(ByVal objects As Object()) Implements IEditor.SetSelectedObjects
            SelectedObjects = objects
            Dim firstObject As Object = objects(0)
            InitControls(firstObject)
            Dim t As Type = firstObject.GetType

            For Each iDescriptor As PropertyDescriptor In Properties
                Dim b As Binder = CType(Binders(iDescriptor.Name), Binder)
                b.ControlInterface.Value = t.GetProperty(iDescriptor.Name).GetValue(objects(0), Nothing)
            Next
        End Sub

        Public Sub InitControls(ByVal o As Object)
            If Controls.Count = 0 Then
                Dim tc As TypeConverter = TypeDescriptor.GetConverter(ItemType)

                If Not tc Is Nothing AndAlso tc.GetPropertiesSupported Then
                    Properties = tc.GetProperties(o)
                Else
                    Properties = TypeDescriptor.GetProperties(ItemType)
                End If

                For Each iDescriptor As PropertyDescriptor In Properties
                    Dim a As DefaultValuesAttribute = CType(iDescriptor.Attributes(GetType(DefaultValuesAttribute)), DefaultValuesAttribute)
                    Dim d As Definition = CType(Definitions(iDescriptor.PropertyType), Definition)
                    Dim b As New Binder
                    b.PropertyControl = Me
                    b.Definition = d
                    b.PropertyName = iDescriptor.Name
                    Binders(b.PropertyName) = b

                    If Not a Is Nothing Then
                        b.DefaultValues = a.DefaultValues
                    Else
                        b.DefaultValues = b.Definition.DefaultValues
                    End If

                    b.ControlInstance = CType(Activator.CreateInstance(b.Definition.ControlType), Control)
                    b.ControlInterface = CType(b.ControlInstance, IEditorControl)
                    b.ControlInstance.Width = Width
                    b.ControlInstance.Text = iDescriptor.DisplayName
                    b.ControlInstance.Anchor = AnchorStyles.Left Or AnchorStyles.Right Or AnchorStyles.Top

                    Dim top As Integer = 0

                    For Each iControl As Control In Controls
                        top += iControl.Height + 5
                    Next

                    b.ControlInstance.Top = top

                    For Each iPropValue As String In b.DefaultValues.Split(","c)
                        If iPropValue <> "" Then
                            Dim pi As PropertyInfo = b.Definition.ControlType.GetProperty(StringHelp.GetLeft(iPropValue, "="))
                            pi.SetValue(b.ControlInstance, TypeDescriptor.GetConverter(pi.PropertyType).ConvertFrom(StringHelp.GetRight(iPropValue, "=")), Nothing)
                        End If
                    Next

                    AddHandler b.ControlInterface.ValueChanged, AddressOf b.ControlValueChanged
                    Controls.Add(b.ControlInstance)
                Next
            End If
        End Sub

        Public Class Definition
            Public ValueType As Type
            Public ControlType As Type
            Public DefaultValues As String

            Public Sub New(ByVal valueType As Type, ByVal controlType As Type, ByVal defaultValues As String)
                Me.ValueType = valueType
                Me.ControlType = controlType
                Me.DefaultValues = defaultValues
            End Sub
        End Class

        Public Class Binder
            Public Definition As Definition
            Public PropertyName As String
            Public ControlInstance As Control
            Public ControlInterface As IEditorControl
            Public PropertyControl As PropertyControl
            Public DefaultValues As String

            Public Sub ControlValueChanged()
                For Each i As Object In PropertyControl.SelectedObjects
                    i.GetType.GetProperty(PropertyName).SetValue(i, ControlInterface.Value, Nothing)
                Next

                PropertyControl.RaiseValueChanged()
            End Sub
        End Class
    End Class
End Namespace