The client configuration runtime will use (in order) the following methods to serialize/deserialize setting values
1) The sheme indicated by the SettingsSerializeAs attribute
2) TypeConverter.To/FromString() for the type converter associated with the type of the setting if it exists and supports converting to/from strings
3) XML Serialization
If you want to use a type that doesn't have the appropriate type converter and doesn't support XML serialization well (which happens to be true for quite a few of the collection types) you can still take advantage of client config by providing your own type converter. Below is a sample that (hopefully) will show what can be done. The sample uses the base64 encoded representation of the binary serialized values as the string representation for the value - this is not exactly easily read by humans, so depending on your specific requirements, you may want to use a different string representation of your data...
Option Strict On
Module Module1
Public Sub Main()
' Associate a type descriptor with the setting type. This could also be done by putting a TypeDescriptionProvider attribute
' on the type declaration itself.
System.ComponentModel.TypeDescriptor.AddProvider( _
New SerializableTypeDescriptionProvider(Of System.Collections.Generic.Dictionary(Of String, String)), _
GetType(System.Collections.Generic.Dictionary(Of String, String)))
' Create an instance of my test class
Dim s As New MyTestSettingsClass
s.Foo = New System.Collections.Generic.Dictionary(Of String, String)
s.Foo.Add("Bar", "Zoo")
s.Foo.Add("Doo", "Goo")
s.Save()
' Load the same settings again...
Dim s2 As New MyTestSettingsClass
Debug.WriteLine(s2.Foo.Count)
End Sub
''' <summary>
'''
''' </summary>
''' <typeparam name="T"></typeparam>
''' <remarks></remarks>
Public Class SerializableTypeDescriptionProvider(Of T)
Inherits System.ComponentModel.TypeDescriptionProvider
Public Overrides Function GetTypeDescriptor(ByVal objectType As System.Type, ByVal instance As Object) As System.ComponentModel.ICustomTypeDescriptor
If Not objectType Is GetType(T) Then
Return MyBase.GetTypeDescriptor(objectType, instance)
End If
Dim parentTypeDescriptor As System.ComponentModel.ICustomTypeDescriptor = MyBase.GetTypeDescriptor(objectType, instance)
If parentTypeDescriptor IsNot Nothing Then
Dim existingTypeConverter As System.ComponentModel.TypeConverter = _
parentTypeDescriptor.GetConverter()
If existingTypeConverter IsNot Nothing _
AndAlso existingTypeConverter.CanConvertFrom(GetType(String)) _
AndAlso existingTypeConverter.CanConvertTo(GetType(String)) _
Then
' If the parentdescriptor already supported to/from string, we might as well
' use that... We don't really *have* to check this, but...
Return MyBase.GetTypeDescriptor(objectType, instance)
End If
End If
Return New SerializableConverterProvider(Of T)(parentTypeDescriptor)
End Function
End Class
' Simple generic implementation that will provide a type converter
' that supports to/from string using base64 encoded string based on
' binary serialization
Public Class SerializableConverterProvider(Of T)
Inherits System.ComponentModel.CustomTypeDescriptor
Sub New(ByVal parentTypeDescriptor As System.ComponentModel.ICustomTypeDescriptor)
MyBase.New(parentTypeDescriptor)
End Sub
Public Overrides Function GetConverter() As System.ComponentModel.TypeConverter
Return New SerializeAsStringTypeConverter()
End Function
Private Class SerializeAsStringTypeConverter
Inherits System.ComponentModel.TypeConverter
Public Overrides Function CanConvertFrom(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal sourceType As System.Type) As Boolean
If sourceType Is GetType(String) Then
Return True
Else
Return MyBase.CanConvertFrom(context, sourceType)
End If
End Function
Public Overrides Function CanConvertTo(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal destinationType As System.Type) As Boolean
If destinationType Is GetType(String) Then
Return True
Else
Return MyBase.CanConvertTo(context, destinationType)
End If
End Function
Public Overrides Function ConvertTo(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal culture As System.Globalization.CultureInfo, ByVal value As Object, ByVal destinationType As System.Type) As Object
If destinationType Is GetType(String) Then
Dim serializer As New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter()
Dim stream As New System.IO.MemoryStream
serializer.Serialize(stream, value)
Return System.Convert.ToBase64String(stream.ToArray())
Else
Return MyBase.ConvertTo(context, culture, value, destinationType)
End If
End Function
Public Overrides Function ConvertFrom(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal culture As System.Globalization.CultureInfo, ByVal value As Object) As Object
If TypeOf value Is String Then
Dim serializer As New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter()
Dim bytes() As Byte = System.Convert.FromBase64String(DirectCast(value, String))
Dim stream As New System.IO.MemoryStream(bytes)
Return serializer.Deserialize(stream)
Else
Return MyBase.ConvertFrom(context, culture, value)
End If
End Function
End Class
End Class
' Since the settings designer isn't very good at handling generic types,
' I have created a (very) simplistic class that expose a setting of a type
' that ordinarily would have been problematic to persist...
Public Class MyTestSettingsClass
Inherits System.Configuration.ApplicationSettingsBase
<System.Configuration.UserScopedSetting()> _
Public Property Foo() As System.Collections.Generic.Dictionary(Of String, String)
Get
Return CType(Me("Foo"), System.Collections.Generic.Dictionary(Of String, String))
End Get
Set(ByVal value As System.Collections.Generic.Dictionary(Of String, String))
Me("Foo") = value
End Set
End Property
End Class
End Module
This posting is provided "AS IS" with no warranties, and confers no rights.