40.6. Custom Resources

Although Visual Studio provides good support for international application development using resource files, there are times when it is not possible to get the level of control required using the default behavior. This section delves a little deeper into how you can serialize custom objects to the resource file and how you can generate designer files, which give you strongly typed accessor methods for resource files you have created.

Visual Studio 2008 enables you to store strings, images, icons, audio files, and other files within a resource file. You can do all this using the rich user interface provided. To store a more complex data type within a resource file you need to serialize it into a string representation that can be included within the resource file.

The first step in adding any data type to a resource file is to make that data type serializable. You can do this easily by marking the class with the Serializable attribute. Once it is marked as serializable, you can add the object to a resource file using an implementation of the IResourceWriter interface — for example, ResXResourceWriter:

<Serializable()> _
Public Class Person
    Public Name As String
    Public Height As Integer
    Public Weight As Double
End Class
Dim p As New Person
p.Name = "Bob"
p.Height = 167
p.Weight = 69.5

Dim rWriter As New ResXResourceWriter("foo.resx")
rWriter.AddResource("DefaultPerson", p)
rWriter.Close()

Serializing an object this way has a couple of drawbacks, however:

  • You need to use code to write out this resource file before the build process so that the resource file can be included in the application. Clearly this is an administrative nightmare, as it is an additional stage in the build process.

  • Furthermore, the serialized representation of the class is a binary blob and is not human-readable. The assumption here is that what is written in the generating code is correct. Unfortunately, this is seldom the case, and it would be easier if the content could be human-readable within Visual Studio 2008.

A workaround for both of these issues is to define a TypeConverter for the class and use that to represent the class as a string. This way, the resource can be edited within the Visual Studio resource editor. TypeConverters provide a mechanism through which the framework can determine whether it is possible to represent a class (in this case a Person class) as a different type (in this case as a string). The first step is to create a TypeConverter using the ExpandableObjectConverter, as follows:

Imports System.ComponentModel
Imports System.ComponentModel.Design.Serialization
Imports System.Globalization

Public Class PersonConverter
    Inherits ExpandableObjectConverter

    Public Overrides Function CanConvertFrom(ByVal context As _
                                                ITypeDescriptorContext, _
                                                ByVal t As Type) As Boolean
        If t Is GetType(String) Then Return True
        Return MyBase.CanConvertFrom(context, t)
    End Function
    Public Overrides Function ConvertFrom( _
                                        ByVal context As ITypeDescriptorContext, _
                                        ByVal info As CultureInfo, _
                                        ByVal value As Object) As Object
        If (TypeOf (value) Is String) Then
            Try
                If value Is Nothing Then Return New Person()
                Dim vals = CStr(value).Split(","c)
                If vals.Length <> 3 Then Return New Person()
                Return New Person With {.Name = vals(0), _
                                        .Height = Integer.Parse(vals(1)), _
                                        .Weight = Double.Parse(vals(2))}
            Catch
                Throw New ArgumentException("Can not convert '" & _
                                                value.ToString & _
                                                "' to type Person")
            End Try
        End If
        Return MyBase.ConvertFrom(context, info, value)
    End Function

    Public Overrides Function ConvertTo(ByVal context As ITypeDescriptorContext, _
                                        ByVal culture As CultureInfo, _
                                        ByVal value As Object, _
                                        ByVal destType As Type) As Object
        If (destType Is GetType(String) And TypeOf (value) Is Person) Then
            Dim c = TryCast(value, Person)
            Return c.Name & "," & c.Height.ToString & "," & c.Weight.ToString
        End If
        Return MyBase.ConvertTo(context, culture, value, destType)
    End Function
End Class

The class being represented also needs to be attributed with the TypeConverter attribute:

<System.ComponentModel.TypeConverter(GetType(PersonConverter))> _
<Serializable()> _
Public Class Person
    Public Name As String
    Public Height As Integer
    Public Weight As Double
End Class

Now you can add this item to a resource file using the string representation of the class. For example, an entry in the resx file might look like this:

<assembly alias=" WindowsApplication1" name="WindowsApplication1, Version=1.0.0.0,
   Culture=neutral, PublicKeyToken=null" />
<data name="Manager" type=" WindowsApplication1.Person, WindowsApplication1">
    <value>Joe, 175, 69.5</value>
</data>

Creating custom resource types is a difficult process, as Visual Studio 2008 doesn't refresh your TypeConverter after it has been loaded the first time. You can either strongly name the assembly in which the TypeConverter is located and increment the version number each time you change it, or you will have to restart Visual Studio in order for the changes to take effect.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.86.211