Trabalhar com arquivos .resx de forma programática
Observação
Este artigo se aplica a .NET Framework. Para obter informações que se aplicam ao .NET 5+ (incluindo o .NET Core), consulte Recursos em arquivos .resx.
Como arquivos de recurso XML (.resx) devem conter XML bem definido, incluindo um cabeçalho que deve seguir um esquema específico seguido por dados em pares nome/valor, você pode concluir que criar esses arquivo manualmente é algo propenso a erros. Como alternativa, você pode criar arquivos .resx com programação usando tipos e membros da biblioteca de classes .NET. Você também pode usar a biblioteca de classes .NET para recuperar os recursos que são armazenados em arquivos .resx. Este artigo explica como você pode usar os tipos e membros no namespace System.Resources para trabalhar com arquivos .resx.
Este artigo discute como trabalhar com arquivos XML (.resx) que contêm recursos. Para obter informações sobre como trabalhar com arquivos de recurso binário que foram inseridos em assemblies, confira ResourceManager.
Aviso
Também há outras maneiras de trabalhar com arquivos .resx além de programação. Quando você adiciona um arquivo de recurso a um projeto do Visual Studio, o Visual Studio fornece uma interface para criar e manter um arquivo .resx e converte automaticamente o arquivo .resx em um arquivo .resources no tempo de compilação. Você também pode usar um editor de texto para manipular um arquivo .resx diretamente. No entanto, para evitar danificar o arquivo, tenha cuidado para não modificar nenhuma informação binária armazenada nele.
Criar um arquivo .resx
Você pode usar a classe System.Resources.ResXResourceWriter para criar um arquivo .resx com programação seguindo estas etapas:
Crie uma instância de um objeto ResXResourceWriter chamando o método ResXResourceWriter(String) e fornecendo o nome do arquivo .resx. O nome do arquivo deve incluir a extensão .resx. Se você criar uma instância do objeto ResXResourceWriter em um bloco
using
, não será necessário chamar explicitamente o método ResXResourceWriter.Close na etapa 3.Chame o método ResXResourceWriter.AddResource para cada recurso que você deseja adicionar ao arquivo. Use as sobrecargas desse método para adicionar cadeia de caracteres, objetos e dados binários (matriz de bytes). Se o recurso for um objeto, ele deverá ser serializável.
Chame o método ResXResourceWriter.Close para gerar o arquivo de recurso e liberar todos os recursos. Se o objeto ResXResourceWriter foi criado em um bloco
using
, os recursos são gravados para o arquivo .resx e os recursos usados pelo objeto ResXResourceWriter são liberados ao final do blocousing
.
O arquivo .resx resultante tem o cabeçalho apropriado e uma marca data
para cada recurso adicionado pelo método ResXResourceWriter.AddResource.
Aviso
Não use arquivos de recurso para armazenar senhas, informações confidenciais ou dados privados.
O exemplo a seguir cria um arquivo .resx chamado CarResources.resx que armazena seis cadeias de caracteres, um ícone e dois objetos definidos pelo aplicativo (dois objetos Automobile
). A classe Automobile
, que é definida e instanciada no exemplo, é marcada com o atributo SerializableAttribute.
using System;
using System.Drawing;
using System.Resources;
[Serializable()] public class Automobile
{
private string carMake;
private string carModel;
private int carYear;
private int carDoors;
private int carCylinders;
public Automobile(string make, string model, int year) :
this(make, model, year, 0, 0)
{ }
public Automobile(string make, string model, int year,
int doors, int cylinders)
{
this.carMake = make;
this.carModel = model;
this.carYear = year;
this.carDoors = doors;
this.carCylinders = cylinders;
}
public string Make {
get { return this.carMake; }
}
public string Model {
get {return this.carModel; }
}
public int Year {
get { return this.carYear; }
}
public int Doors {
get { return this.carDoors; }
}
public int Cylinders {
get { return this.carCylinders; }
}
}
public class Example
{
public static void Main()
{
// Instantiate an Automobile object.
Automobile car1 = new Automobile("Ford", "Model N", 1906, 0, 4);
Automobile car2 = new Automobile("Ford", "Model T", 1909, 2, 4);
// Define a resource file named CarResources.resx.
using (ResXResourceWriter resx = new ResXResourceWriter(@".\CarResources.resx"))
{
resx.AddResource("Title", "Classic American Cars");
resx.AddResource("HeaderString1", "Make");
resx.AddResource("HeaderString2", "Model");
resx.AddResource("HeaderString3", "Year");
resx.AddResource("HeaderString4", "Doors");
resx.AddResource("HeaderString5", "Cylinders");
resx.AddResource("Information", SystemIcons.Information);
resx.AddResource("EarlyAuto1", car1);
resx.AddResource("EarlyAuto2", car2);
}
}
}
Imports System.Drawing
Imports System.Resources
<Serializable()> Public Class Automobile
Private carMake As String
Private carModel As String
Private carYear As Integer
Private carDoors AS Integer
Private carCylinders As Integer
Public Sub New(make As String, model As String, year As Integer)
Me.New(make, model, year, 0, 0)
End Sub
Public Sub New(make As String, model As String, year As Integer,
doors As Integer, cylinders As Integer)
Me.carMake = make
Me.carModel = model
Me.carYear = year
Me.carDoors = doors
Me.carCylinders = cylinders
End Sub
Public ReadOnly Property Make As String
Get
Return Me.carMake
End Get
End Property
Public ReadOnly Property Model As String
Get
Return Me.carModel
End Get
End Property
Public ReadOnly Property Year As Integer
Get
Return Me.carYear
End Get
End Property
Public ReadOnly Property Doors As Integer
Get
Return Me.carDoors
End Get
End Property
Public ReadOnly Property Cylinders As Integer
Get
Return Me.carCylinders
End Get
End Property
End Class
Module Example
Public Sub Main()
' Instantiate an Automobile object.
Dim car1 As New Automobile("Ford", "Model N", 1906, 0, 4)
Dim car2 As New Automobile("Ford", "Model T", 1909, 2, 4)
' Define a resource file named CarResources.resx.
Using resx As New ResXResourceWriter(".\CarResources.resx")
resx.AddResource("Title", "Classic American Cars")
resx.AddResource("HeaderString1", "Make")
resx.AddResource("HeaderString2", "Model")
resx.AddResource("HeaderString3", "Year")
resx.AddResource("HeaderString4", "Doors")
resx.AddResource("HeaderString5", "Cylinders")
resx.AddResource("Information", SystemIcons.Information)
resx.AddResource("EarlyAuto1", car1)
resx.AddResource("EarlyAuto2", car2)
End Using
End Sub
End Module
Dica
Você também pode usar o Visual Studio para criar arquivos .resx. No tempo de compilação, o Visual Studio usa o Gerador de Arquivo de Recurso (Resgen.exe) para converter o arquivo .resx em um arquivo de recurso binário (.resources), além de inseri-lo em um assembly de aplicativo ou um assembly satélite.
Não é possível inserir um arquivo .resx em um executável do runtime ou compilá-lo em um assembly satélite. Você deve converter seu arquivo .resx em um arquivo de recurso binário (.resources) usando o Gerador de Arquivo de Recurso (Resgen.exe). O arquivo .resources resultante poderá então ser inserido em um assembly de aplicativo ou um assembly satélite. Para obter mais informações, confira Criar arquivos de recursos.
Enumerar recursos
Em alguns casos, pode ser útil recuperar todos os recursos, em vez de um recurso específico, de um arquivo .resx. Para fazer isso, você pode usar a classe System.Resources.ResXResourceReader, que fornece um enumerador para todos os recursos no arquivo .resx. A classe System.Resources.ResXResourceReader implementa IDictionaryEnumerator, que retorna um objeto DictionaryEntry que representa um recurso específico para cada iteração do loop. Sua propriedade DictionaryEntry.Key retorna a chave do recurso e sua DictionaryEntry.Value propriedade retorna o valor do recurso.
O exemplo a seguir cria um objeto ResXResourceReader para o arquivo CarResources.resx criado no exemplo anterior e itera por meio do arquivo de recurso. Ele adiciona os dois objetos Automobile
que são definidos no arquivo de recurso para um objeto System.Collections.Generic.List<T> e adiciona cinco das seis cadeias de caracteres a um objeto SortedList. Os valores do objeto SortedList são convertidos em uma matriz de parâmetro, que é usada para exibir os títulos de coluna para o console. Os valores da propriedade Automobile
também são exibidos no console.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Resources;
public class Example
{
public static void Main()
{
string resxFile = @".\CarResources.resx";
List<Automobile> autos = new List<Automobile>();
SortedList headers = new SortedList();
using (ResXResourceReader resxReader = new ResXResourceReader(resxFile))
{
foreach (DictionaryEntry entry in resxReader) {
if (((string) entry.Key).StartsWith("EarlyAuto"))
autos.Add((Automobile) entry.Value);
else if (((string) entry.Key).StartsWith("Header"))
headers.Add((string) entry.Key, (string) entry.Value);
}
}
string[] headerColumns = new string[headers.Count];
headers.GetValueList().CopyTo(headerColumns, 0);
Console.WriteLine("{0,-8} {1,-10} {2,-4} {3,-5} {4,-9}\n",
headerColumns);
foreach (var auto in autos)
Console.WriteLine("{0,-8} {1,-10} {2,4} {3,5} {4,9}",
auto.Make, auto.Model, auto.Year,
auto.Doors, auto.Cylinders);
}
}
// The example displays the following output:
// Make Model Year Doors Cylinders
//
// Ford Model N 1906 0 4
// Ford Model T 1909 2 4
Imports System.Collections
Imports System.Collections.Generic
Imports System.Resources
Module Example
Public Sub Main()
Dim resxFile As String = ".\CarResources.resx"
Dim autos As New List(Of Automobile)
Dim headers As New SortedList()
Using resxReader As New ResXResourceReader(resxFile)
For Each entry As DictionaryEntry In resxReader
If CType(entry.Key, String).StartsWith("EarlyAuto") Then
autos.Add(CType(entry.Value, Automobile))
Else If CType(entry.Key, String).StartsWith("Header") Then
headers.Add(CType(entry.Key, String), CType(entry.Value, String))
End If
Next
End Using
Dim headerColumns(headers.Count - 1) As String
headers.GetValueList().CopyTo(headerColumns, 0)
Console.WriteLine("{0,-8} {1,-10} {2,-4} {3,-5} {4,-9}",
headerColumns)
Console.WriteLine()
For Each auto In autos
Console.WriteLine("{0,-8} {1,-10} {2,4} {3,5} {4,9}",
auto.Make, auto.Model, auto.Year,
auto.Doors, auto.Cylinders)
Next
End Sub
End Module
' The example displays the following output:
' Make Model Year Doors Cylinders
'
' Ford Model N 1906 0 4
' Ford Model T 1909 2 4
Recuperar um recurso específico
Além de enumerar os itens em um arquivo .resx, você pode recuperar um recurso específico por nome usando a classe System.Resources.ResXResourceSet. O método ResourceSet.GetString(String) recupera o valor de um recurso de cadeia de caracteres nomeado. O método ResourceSet.GetObject(String) recupera o valor de um objeto nomeado ou de dados binários. O método retorna um objeto que deve ser convertido (em C# ou no Visual Basic) em um objeto do tipo apropriado.
O exemplo a seguir recupera o ícone e a cadeia de caracteres de legenda de um formulário pelos nomes dos recursos. Ele também recupera objetos Automobile
definido pelo aplicativo usados no exemplo anterior e exibe-os em um controle DataGridView.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Resources;
using System.Windows.Forms;
public class CarDisplayApp : Form
{
private const string resxFile = @".\CarResources.resx";
Automobile[] cars;
public static void Main()
{
CarDisplayApp app = new CarDisplayApp();
Application.Run(app);
}
public CarDisplayApp()
{
// Instantiate controls.
PictureBox pictureBox = new PictureBox();
pictureBox.Location = new Point(10, 10);
this.Controls.Add(pictureBox);
DataGridView grid = new DataGridView();
grid.Location = new Point(10, 60);
this.Controls.Add(grid);
// Get resources from .resx file.
using (ResXResourceSet resxSet = new ResXResourceSet(resxFile))
{
// Retrieve the string resource for the title.
this.Text = resxSet.GetString("Title");
// Retrieve the image.
Icon image = (Icon) resxSet.GetObject("Information", true);
if (image != null)
pictureBox.Image = image.ToBitmap();
// Retrieve Automobile objects.
List<Automobile> carList = new List<Automobile>();
string resName = "EarlyAuto";
Automobile auto;
int ctr = 1;
do {
auto = (Automobile) resxSet.GetObject(resName + ctr.ToString());
ctr++;
if (auto != null)
carList.Add(auto);
} while (auto != null);
cars = carList.ToArray();
grid.DataSource = cars;
}
}
}
Imports System.Collections.Generic
Imports System.Drawing
Imports System.Resources
Imports System.Windows.Forms
Public Class CarDisplayApp : Inherits Form
Private Const resxFile As String = ".\CarResources.resx"
Dim cars() As Automobile
Public Shared Sub Main()
Dim app As New CarDisplayApp()
Application.Run(app)
End Sub
Public Sub New()
' Instantiate controls.
Dim pictureBox As New PictureBox()
pictureBox.Location = New Point(10, 10)
Me.Controls.Add(pictureBox)
Dim grid As New DataGridView()
grid.Location = New Point(10, 60)
Me.Controls.Add(grid)
' Get resources from .resx file.
Using resxSet As New ResXResourceSet(resxFile)
' Retrieve the string resource for the title.
Me.Text = resxSet.GetString("Title")
' Retrieve the image.
Dim image As Icon = CType(resxSet.GetObject("Information", True), Icon)
If image IsNot Nothing Then
pictureBox.Image = image.ToBitmap()
End If
' Retrieve Automobile objects.
Dim carList As New List(Of Automobile)
Dim resName As String = "EarlyAuto"
Dim auto As Automobile
Dim ctr As Integer = 1
Do
auto = CType(resxSet.GetObject(resName + ctr.ToString()), Automobile)
ctr += 1
If auto IsNot Nothing Then carList.Add(auto)
Loop While auto IsNot Nothing
cars = carList.ToArray()
grid.DataSource = cars
End Using
End Sub
End Class
Converter arquivos .resx em arquivos .resources binários
Converter arquivos .resx em arquivos de recurso binário inserido (.resources) traz vantagens significativas. Embora os arquivos .resx sejam fáceis de ler e manter durante o desenvolvimento do aplicativo, eles raramente são incluídos em aplicativos acabados. Se eles forem distribuídos com um aplicativo, existirão como arquivos separados do executável do aplicativo e as bibliotecas que o acompanham. Por outro lado, arquivos .resources são inseridos no executável do aplicativo ou nos assemblies que os acompanham. Além disso, para aplicativos localizados, contar com arquivos .resx no tempo de execução coloca a responsabilidade de manipular o fallback de recurso nas mãos do desenvolvedor. Por outro lado, se um conjunto de assemblies de satélite que contêm arquivos .resources inseridos tiver sido criado, o Common Language Runtime manipula o processo de fallback de recurso.
Para converter um arquivo .resx em um arquivo .resources, use o Gerador de Arquivo de Recurso (resgen.exe), que tem a seguinte sintaxe básica:
resgen.exe .resxFilename
O resultado é um arquivo de recurso binário que tem o mesmo nome de arquivo raiz que o arquivo .resx e uma extensão de arquivo .resources. Esse arquivo poderá ser compilado em um executável ou uma biblioteca no tempo de compilação. Se você estiver usando o compilador do Visual Basic, use a sintaxe a seguir para inserir um arquivo .resources no executável do aplicativo:
vbc filename .vb -resource: .resourcesFilename
Se você usar C#, a sintaxe será a seguinte:
csc filename .cs -resource: .resourcesFilename
O arquivo .resources também pode ser incorporado em um assembly satélite usando o Vinculador de Assembly (al.exe), que tem a seguinte sintaxe básica:
al resourcesFilename -out: assemblyFilename