﻿Option Strict On
Public Class LayoutSerializer
    Public Enum LoadMode
        CreateAllControls
        DontCreateExistingControls
        OnlyCreateExistingControls
    End Enum

    Public Shared Sub SaveLayout(ByVal instance As Form, ByVal outputStream As System.IO.Stream)
        Dim ControlInfoList As New List(Of ControlInfo)

        Dim formatter As System.Runtime.Serialization.IFormatter = New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter

        Dim ctl As Control = instance.GetNextControl(instance, True)
        ControlInfoList.Add(New ControlInfo(instance))
        Do Until ctl Is Nothing
            ControlInfoList.Add(New ControlInfo(ctl))
            ctl = instance.GetNextControl(ctl, True)
        Loop
        formatter.Serialize(outputStream, ControlInfoList)
    End Sub

    Public Shared Sub LoadLayout(ByVal instance As Form, ByVal inputStream As System.IO.Stream, ByVal Mode As LoadMode)
        Dim ControlInfoList As List(Of ControlInfo)
        Dim formatter As System.Runtime.Serialization.IFormatter = New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
        Dim ctrl As Control
        Dim propEnum As IEnumerator
        Dim dictEntry As System.Collections.Generic.KeyValuePair(Of String, Object)
        ControlInfoList = CType(formatter.Deserialize(inputStream), List(Of ControlInfo))
        Dim ci As ControlInfo
        ci = ControlInfoList.Item(0)
        propEnum = ci.Properties.GetEnumerator
        Do While propEnum.MoveNext
            dictEntry = CType(propEnum.Current, System.Collections.Generic.KeyValuePair(Of String, Object))
            ci.ControlType.GetProperty(dictEntry.Key).SetValue(instance, dictEntry.Value, Nothing)
        Loop
        For i As Integer = 1 To ControlInfoList.Count - 1
            ci = ControlInfoList.Item(i)
            ctrl = CType(Activator.CreateInstance(ci.ControlType, True), Control)
            propEnum = ci.Properties.GetEnumerator
            Do While propEnum.MoveNext
                dictEntry = CType(propEnum.Current, System.Collections.Generic.KeyValuePair(Of String, Object))
                ci.ControlType.GetProperty(dictEntry.Key).SetValue(ctrl, dictEntry.Value, Nothing)
            Loop
            Select Case Mode
                Case LoadMode.DontCreateExistingControls
                    If Not instance.Controls.ContainsKey(ctrl.Name) Then
                        instance.Controls.Add(ctrl)
                    Else
                        propEnum.Reset()
                        Dim existingControl As Control = instance.Controls(ctrl.Name)
                        Do While propEnum.MoveNext
                            dictEntry = CType(propEnum.Current, System.Collections.Generic.KeyValuePair(Of String, Object))
                            existingControl.GetType.GetProperty(dictEntry.Key).SetValue(existingControl, dictEntry.Value, Nothing)
                        Loop
                    End If
                Case LoadMode.CreateAllControls
                    instance.Controls.Add(ctrl)
                Case LoadMode.OnlyCreateExistingControls

                    If instance.Controls.ContainsKey(ctrl.Name) Then
                        instance.Controls.Add(ctrl)
                    End If
            End Select
        Next
    End Sub

    <Serializable()> _
    Private Class ControlInfo
        Public ControlType As Type
        Public Properties As New Dictionary(Of String, Object)
        Sub New(ByVal ctrl As Control)
            ControlType = ctrl.GetType
            Dim value As Object
            For Each pi As Reflection.PropertyInfo In ControlType.GetProperties
                If pi.CanWrite Then
                    value = pi.GetValue(ctrl, Nothing)
                    If value Is Nothing OrElse value.GetType.IsSerializable Then
                        Properties.Add(pi.Name, value)
                    End If
                End If
            Next
        End Sub
    End Class
End Class
