饭饭TXT > 学习管理 > 《Visual Studio 2010 高级编程(英文出书版)》作者:Nick Randolph/等【完结】 > [Visual.Studio.2010.高级编程].Professional.Visual.Studio.2010.txt

第 121 页

作者:Nick Randolph/等 当前章节:15381 字 更新时间:2026-6-18 14:51

object was used to retrieve resources associated with the form. The ComponentResourceManager

extends the base ResourceManager by providing additional functionality for retrieving and applying

component properties. Here are the original four lines required to set the properties defined for

Button1:

Me.Button1.Location = New Point(71, 43)

Me.Button1.Size = New Size(185, 166)

Me.Button1.Text = "Button1"

Me.Button1.TabIndex = 0

Coding resource files .

817

Using the ComponentResourceManager, they can be condensed into just one line:

resources.ApplyResources(Me.Button1, "Button1")

In previous versions of Visual Studio, the code generated when localization was turned on was much

more verbose. For each property, a separate call was made to the ResourceManager to retrieve it by

name, as shown in this code snippet:

Me.Button1.Location = CType(resources.GetObject("Button1.Location"), Point)

Me.Button1.Size = CType(resources.GetObject("Button1.Size"), Size)

Me.Button1.TabIndex = CType(resources.GetObject("Button1.TabIndex"), Integer)

Me.Button1.Text = resources.GetString("Button1.Text")

It is still possible to write this code because the GetObject method is still available on the

ComponentResourceManager. The issue with writing this code is that each property that is going

to be localized needs to be known at compile time. Because of this, every property on every control

was added to the resource file. This added excess properties (even when they were no different from

the default values) to the resource file. It also added huge overhead during the loading up of a form,

because each property was set via a resource property.

The ApplyResources method in the ComponentResourceManager class works in reverse. When you

specify a control name, which must be unique on a form, all resources that start with that prefix are

extracted. The full resource name is then used to determine the property to set on the control. For

example, a resource with the name Button1.Location would be extracted for the control called

Button1, and the value used to set the Location property on that control.

This process eliminates the need to have all properties specified in a resource file. It also creates the

need for culture resource files to specify additional properties that might not have been defined in

the default resource file.

You might be wondering whether any additional penalties exist in using the

ComponentResourceManager. To set a property on a control using the name of the property,

the ComponentResourceManager uses reflection to find the appropriate property. Once it has

been retrieved, it can be invoked. Each search that is done in order to set the property is relatively

expensive. However, given the reduced number of properties to be set, the tradeoff is definitely

worthwhile, because the application can easily be localized without recompilation of the main

application.

codinG resource files

In addition to the rich visual tools that Visual Studio 2010 now provides for editing resource files,

it is possible to use code to create resource files. The .NET Framework provides support for reading

and writing resource files using two interfaces: IResourceReader and IResourceWriter. Once the

resource files have been created, they need to be added to the application or manually linked so that

they can be referenced within the application.

818

.

chaPter 38 reSource FileS

.

IResource Reader: The reader interface ensures that resource readers have the following

methods:

.

GetEnumerator: The GetEnumerator method retrieves an IDictionaryEnumerator

object that permits the developer to iterate over each of the resources in the resource file.

.

Close: The Close method is used to close the resource reader and release any

associated resources.

.

IResource Writer: The writer interface ensures that resource writers have the following

methods:

.

AddResource: Three overloads to the AddResource method support adding

resources to the resource file. Both of the framework implementations of this

interface have either an additional overload of this method or an alternative

method for adding resources. The overloads that are part of this interface support

adding resources in a name-value pair. Each method has the resource name as the

first parameter and a value, such as a string, byte array, or object, as the second

parameter. The final implementation that takes an object as a parameter may need

to be serializable or converted to a string via a type converter.

.

Close: The Close method writes resources out to the stream before closing it.

.

Generate: Unlike the Close method, the Generate method simply writes the

resources out to the stream without closing it. Once this method is called, any other

method will cause an exception to be raised.

resourcereader and resourcewriter

ResourceReader and ResourceWriter are an implementation of the IResource interfaces to support

reading and writing directly to resources files. Although reading and writing to this format is the

most direct approach, because it reduces the need to use Resgen to generate the resources file, it

does limit the quality of information that can be retrieved in reading from the file. Each resource is

treated as a series of bytes where the type is unknown.

resxresourcereader and resxresourcewriter

ResxResourceReader and ResxResourceWriter are more versatile implementations of the IResource

interfaces. In addition to supporting the IResource interface, ResxResourceWriter supports an

additional overload of the AddResource method, whereby a ResxDataNode can be added. A

ResxDataNode is very similar to a dictionary entry, because it has a key (in this case, the Name

property) and a value (which you must set when the node is created). However, the difference is that

this node can support additional properties such as a comment and, as an alternative to a value, a

file reference (for example, one that indicates where an image needs to be added to a resource file).

As mentioned previously, it is possible to add a file reference to a resx file so that the file is still

editable, yet has the benefit of being compiled into the resource file by resgen.exe. The supporting

class in the framework is ResxFileRef. This can be instantiated and added as a resource via the

ResxResourceWriter. This inserts an XML node similar to the following snippet:

Custom resources .

819

< data name="Figure_11_2" type="ResXFileRef, System.Windows.Forms" >

< value > .\Resources\CompanyLogo.tif;System.Drawing.Bitmap, System.Drawing,

Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a < /value >

>data/<

Resource files are the best means of storing static application data. Although

they are linked in to the application as part of the compilation process, their

contents can easily be extracted and made human-readable. Because of this,

however, resource files are not suitable for storing secure data such as passwords

and credit card information.

custoM resources

Although Visual Studio provides good support for international application development using

resource files, at times 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 2010 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:

Vb

< Serializable() > _

Public Class Person

Public Property Name As String

Public Property Height As Integer

Public Property 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()

820

.

chaPter 38 reSource FileS

c#

[Serializable()]

public class Person{

public string Name { get; set; }

public int Height { get; set; }

public double Weight { get; set; }

}

var p = new Person(){

Name = "Bob",

Height = 167,

Weight = 69.5};

var rWriter = new ResXResourceWriter("foo.resx");

rWriter.AddResource("DefaultPerson", p);

rWriter.Close();

However, serializing an object this way has a couple of drawbacks:

.

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,

because 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 2010.

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:

Vb

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

Custom resources .

821

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

c#

public class PersonConverter : ExpandableObjectConverter{

public override bool CanConvertFrom(ITypeDescriptorContext context,

Type t){

if (typeof(string) == t) return true;

return base.CanConvertFrom(context, t);

}

public override object ConvertFrom(ITypeDescriptorContext context,

CultureInfo culture, object value){

if (value is string){

try{

if (value == null) return new Person();

var vals = (value as string).Split(’,’);

if (vals.Length != 3) return new Person();

return new Person{

Name = vals[0],

Height = int.Parse(vals[1]),

Weight = double.Parse(vals[2])

};

}

catch (Exception){

throw new ArgumentException("Can not convert ’" +

value.ToString() + "’ to type Person");

}

}

822 .

chaPter 38 reSource FileS

return null;

}

public override object ConvertTo(ITypeDescriptorContext context,

CultureInfo culture, object value, Type destType){

if (typeof(string) == destType & & value is Person){

var c = value as Person;

return c.Name + "," + c.Height.ToString() + "," + c.Weight.ToString();

}

return base.ConvertTo(context, culture, value, destType);

}

}

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

Vb

< System.ComponentModel.TypeConverter(GetType(PersonConverter)) > _

< Serializable() > _

Public Class Person

Public Property Name As String

Public Property Height As Integer

Public Property Weight As Double

End Class

c#

[System.ComponentModel.TypeConverter(typeof(PersonConverter))]

[Serializable()]

public class Person{

public string Name { get; set; }

public int Height { get; set; }

public double Weight { get; set; }

}

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="CustomResourceType" name="CustomResourceType, Version=1.0.0.0,

目录
设置
设置
阅读主题
字体风格
雅黑 宋体 楷书 卡通
字体大小
适中 偏大 超大
保存设置
恢复默认
手机
手机阅读
扫码获取链接,使用浏览器打开
书架同步,随时随地,手机阅读
首 页 < 上一章 章节列表 下一章 > 尾 页