Usare i file con estensione resx a livello di codice
Nota
Questo articolo si applica a .NET Framework. Per informazioni che si applicano a .NET 5+ (incluso .NET Core), vedere Risorse nei file con estensione resx.
Poiché i file di risorse XML (con estensione resx) devono essere costituiti da codice XML ben definito, comprensivo di un'intestazione che deve seguire uno schema specifico, seguita da dati in coppie nome/valore, la creazione manuale di questi file è soggetta a errori. In alternativa, è possibile creare file con estensione resx a livello di codice usando tipi e membri inclusi nella libreria di classi .NET. La libreria di classi .NET può essere usata anche per recuperare risorse archiviate in file con estensione resx. Questo articolo illustra come usare i tipi e i membri nello spazio dei nomi System.Resources per i file RESX.
Questo articolo descrive solo l'uso di file XML (con estensione resx) che contengono risorse. Per informazioni sull'uso dei file di risorse binari incorporati negli assembly, vedere ResourceManager.
Avviso
Esistono anche modi per usare i file RESX, oltre all'uso a livello di codice. Quando si aggiunge un file di risorse a un progetto di Visual Studio, Visual Studio fornisce un'interfaccia per la creazione e la gestione di un file RESX e converte automaticamente il file RESX in un file con estensione resources in fase di compilazione. È anche possibile usare un editor di testo per modificare direttamente un file RESX. Prestare comunque attenzione a non modificare le eventuali informazioni binarie archiviate nel file, per evitare di danneggiarlo.
Creare un file con estensione resx
È possibile usare la classe System.Resources.ResXResourceWriter per creare un file RESX a livello di codice seguendo questa procedura:
Creare un'istanza di un oggetto ResXResourceWriter chiamando il metodo ResXResourceWriter(String) e specificando il nome del file RESX. Il nome del file deve includere l'estensione resx. Se si crea un'istanza dell'oggetto ResXResourceWriter in un blocco
using
, non è necessario chiamare esplicitamente il metodo ResXResourceWriter.Close nel passaggio 3.Chiamare il metodo ResXResourceWriter.AddResource per ogni risorsa da aggiungere al file. Usare l'overload di questo metodo per aggiungere dati stringa, oggetto e binari (matrice di byte). Se la risorsa è un oggetto, deve essere serializzabile.
Chiamare il metodo ResXResourceWriter.Close per generare il file di risorse e per rilasciare tutte le risorse. Se l'oggetto ResXResourceWriter è stato creato all'interno di un blocco
using
, le risorse vengono scritte nel file RESX e le risorse usate dall'oggetto ResXResourceWriter vengono rilasciate alla fine del bloccousing
.
Il file RESX risultante ha l'intestazione appropriata e un tag data
per ogni risorsa aggiunta dal metodo ResXResourceWriter.AddResource .
Avviso
Non usare file di risorse per archiviare password, informazioni sensibili per la sicurezza o dati personali.
Nell'esempio seguente viene creato un file con estensione resx denominato CarResources.resx che archivia sei stringhe, un'icona e due oggetti definiti dall'applicazione, ovvero due oggetti Automobile
. La classe Automobile
, che viene definita e di cui viene creata un'istanza nell'esempio, è contrassegnata con l'attributo 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
Suggerimento
Si può anche usare Visual Studio per creare file RESX. In fase di compilazione Visual Studio usa il generatore di file di risorse (Resgen.exe) per convertire il file RESX in una risorsa binaria (con estensione resources) e lo incorpora anche nell'assembly dell'applicazione o in un assembly satellite.
Non è possibile incorporare un file RESX in un eseguibile di runtime o compilarlo in un assembly satellite. Il file RESX deve essere convertito in un file di risorse binario (con estensione resources) usando il generatore di file di risorse (Resgen.exe). Successivamente, il file con estensione resources risultante può essere incorporato in un assembly dell'applicazione o in un assembly satellite. Per altre informazioni, vedere Creare file di risorse.
Enumerare le risorse
In alcuni casi può essere necessario recuperare tutte le risorse anziché una risorsa specifica da un file RESX. A questo scopo, si può usare la classe System.Resources.ResXResourceReader , che fornisce un enumeratore per tutte le risorse nel file RESX. La classe System.Resources.ResXResourceReader implementa IDictionaryEnumerator, che restituisce un oggetto DictionaryEntry che rappresenta una risorsa specifica per ogni iterazione del ciclo. La relativa proprietà DictionaryEntry.Key restituisce la chiave della risorsa e la proprietà DictionaryEntry.Value restituisce il valore della risorsa.
L'esempio seguente consente di creare un oggetto ResXResourceReader per il file CarResources.resx creato nell'esempio precedente e di scorrere il file di risorse. Aggiunge i due oggetti Automobile
definiti nel file di risorse a un oggetto System.Collections.Generic.List<T> e aggiunge cinque delle sei stringhe a un oggetto SortedList . I valori nell'oggetto SortedList vengono convertiti in una matrice di parametri, usata per visualizzare le intestazioni di colonna nella console. Nella console vengono visualizzati anche i valori della proprietà Automobile
.
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
Recuperare una risorsa specifica
Oltre a enumerare gli elementi in un file RESX, è possibile recuperare una risorsa specifica in base al nome tramite la classe System.Resources.ResXResourceSet . Il metodo ResourceSet.GetString(String) recupera il valore di una risorsa di tipo stringa denominata. Il metodo ResourceSet.GetObject(String) recupera il valore di un oggetto denominato o di dati binari. Il metodo restituisce un oggetto di cui deve essere eseguito il cast (in C#) o da convertire (in Visual Basic) in un oggetto del tipo appropriato.
L'esempio seguente recupera l'icona e la stringa della didascalia di un form in base ai nomi delle risorse corrispondenti. Recupera anche gli oggetti Automobile
definiti dall'applicazione usati nell'esempio precedente e li visualizza in un controllo 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
Convertire i file RESX in file binari con estensione resources
La conversione dei file RESX in file di risorse binari incorporati (con estensione resources) presenta vantaggi significativi. Sebbene i file RESX siano facili da leggere e gestire durante lo sviluppo di applicazioni, è raro che siano inclusi nelle applicazioni finite. Se vengono distribuiti con un'applicazione, sono costituiti da file separati indipendenti dall'eseguibile dell'applicazione e dalle librerie associate. Al contrario, i file con estensione resources sono incorporati nell'eseguibile dell'applicazione o negli assembly associati. Inoltre, per le applicazioni localizzate, se si basano su file RESX in fase di esecuzione la responsabilità della gestione del fallback delle risorse ricade sullo sviluppatore. Se invece è stato creato un set di assembly satellite che contengono i file con estensione resources incorporati, il processo di fallback delle risorse viene gestito da Common Language Runtime.
Per convertire un file con estensione resx in un file con estensione resources, usare il generatore di file di risorse (resgen.exe), con la seguente sintassi di base:
resgen.exe .resxFilename
Il risultato è un file di risorse binario con lo stesso nome file radice del file RESX e l'estensione resources. Questo file può quindi essere compilato in un file eseguibile o una libreria in fase di compilazione. Se si usa il compilatore Visual Basic, usare la sintassi seguente per incorporare un file con estensione resources nell'eseguibile di un'applicazione:
vbc filename .vb -resource: .resourcesFilename
Se si usa C#, la sintassi è la seguente:
csc filename .cs -resource: .resourcesFilename
Il file con estensione resources può anche essere incorporato in un assembly satellite tramite Assembly Linker (AL.exe), che ha la seguente sintassi di base:
al resourcesFilename -out: assemblyFilename