Arbeta med .resx-filer programmatiskt

Kommentar

Den här artikeln gäller för .NET Framework. Information som gäller för .NET 5+ (inklusive .NET Core) finns i Resurser i .resx-filer.

Eftersom XML-resursfiler (.resx) måste bestå av väldefinierad XML, inklusive ett huvud som måste följa ett specifikt schema följt av data i namn/värde-par, kan det hända att det är felbenäget att skapa dessa filer manuellt. Alternativt kan du skapa .resx-filer programmatiskt med hjälp av typer och medlemmar i .NET-klassbiblioteket. Du kan också använda .NET-klassbiblioteket för att hämta resurser som lagras i .resx-filer. Den här artikeln beskriver hur du kan använda typerna och medlemmarna i System.Resources namnområdet för att arbeta med .resx-filer.

I den här artikeln beskrivs hur du arbetar med XML-filer (.resx) som innehåller resurser. Information om hur du arbetar med binära resursfiler som har bäddats in i sammansättningar ResourceManagerfinns i .

Varning

Det finns också sätt att arbeta med .resx-filer förutom programmatiskt. När du lägger till en resursfil i ett Visual Studio-projekt tillhandahåller Visual Studio ett gränssnitt för att skapa och underhålla en .resx-fil och konverterar automatiskt .resx-filen till en .resources-fil vid kompileringstillfället. Du kan också använda en textredigerare för att ändra en .resx-fil direkt. Var dock noga med att inte ändra binär information som lagras i filen för att undvika att skada filen.

Skapa en .resx-fil

Du kan använda System.Resources.ResXResourceWriter klassen för att skapa en .resx-fil programmässigt genom att följa dessa steg:

  1. Instansiera ett ResXResourceWriter objekt genom att anropa ResXResourceWriter(String) metoden och ange namnet på .resx-filen. Filnamnet måste innehålla .resx-tillägget. Om du instansierar objektet ResXResourceWriter i ett using block behöver du inte uttryckligen ResXResourceWriter.Close anropa metoden i steg 3.

  2. ResXResourceWriter.AddResource Anropa metoden för varje resurs som du vill lägga till i filen. Använd överlagringarna för den här metoden för att lägga till data om strängar, objekt och binärt värde (bytematris). Om resursen är ett objekt måste den vara serialiserbar.

  3. ResXResourceWriter.Close Anropa metoden för att generera resursfilen och för att frigöra alla resurser. Om objektet ResXResourceWriter skapades i ett using block skrivs resurser till .resx-filen och de resurser som används av ResXResourceWriter objektet släpps i slutet av using blocket.

Den resulterande .resx-filen har rätt rubrik och en data tagg för varje resurs som läggs till av ResXResourceWriter.AddResource metoden.

Varning

Använd inte resursfiler för att lagra lösenord, säkerhetskänslig information eller privata data.

I följande exempel skapas en .resx-fil med namnet CarResources.resx som lagrar sex strängar, en ikon och två programdefinierade objekt (två Automobile objekt). Klassen Automobile , som definieras och instansieras i exemplet, taggas med SerializableAttribute attributet .

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

Dricks

Du kan också använda Visual Studio för att skapa .resx-filer. Vid kompileringstillfället använder Visual Studio resursfilgeneratorn (Resgen.exe) för att konvertera .resx-filen till en binär resursfil (.resources) och bäddar även in den i antingen en programsammansättning eller en satellitsammansättning.

Du kan inte bädda in en .resx-fil i en körbar körning eller kompilera den i en satellitsammansättning. Du måste konvertera .resx-filen till en binär resursfil (.resources) med hjälp av resursfilgeneratorn (Resgen.exe). Den resulterande .resources-filen kan sedan bäddas in i en programsammansättning eller en satellitsammansättning. Mer information finns i Skapa resursfiler.

Räkna upp resurser

I vissa fall kanske du vill hämta alla resurser, i stället för en specifik resurs, från en .resx-fil. För att göra detta kan du använda System.Resources.ResXResourceReader klassen, som tillhandahåller en uppräknare för alla resurser i .resx-filen. Klassen System.Resources.ResXResourceReader implementerar IDictionaryEnumerator, som returnerar ett DictionaryEntry objekt som representerar en viss resurs för varje iteration av loopen. Dess DictionaryEntry.Key egenskap returnerar resursens nyckel och dess DictionaryEntry.Value egenskap returnerar resursens värde.

I följande exempel skapas ett ResXResourceReader objekt för filen CarResources.resx som skapades i föregående exempel och itererar via resursfilen. Det lägger till de två Automobile objekt som definieras i resursfilen till ett System.Collections.Generic.List<T> objekt och lägger till fem av de sex strängarna i ett SortedList objekt. Värdena i SortedList objektet konverteras till en parametermatris som används för att visa kolumnrubriker i konsolen. Egenskapsvärdena Automobile visas också för konsolen.

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

Hämta en specifik resurs

Förutom att räkna upp objekten i en .resx-fil kan du hämta en specifik resurs efter namn med hjälp System.Resources.ResXResourceSet av klassen . Metoden ResourceSet.GetString(String) hämtar värdet för en namngiven strängresurs. Metoden ResourceSet.GetObject(String) hämtar värdet för ett namngivet objekt eller binära data. Metoden returnerar ett objekt som sedan måste konverteras (i C#) eller konverteras (i Visual Basic) till ett objekt av lämplig typ.

I följande exempel hämtas ett formulärs bildtext sträng och ikon efter deras resursnamn. Den hämtar också de programdefinierade Automobile objekt som användes i föregående exempel och visar dem i en DataGridView kontroll.

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

Konvertera .resx-filer till binära .resources-filer

Att konvertera .resx-filer till inbäddade binära resursfiler (.resources) har betydande fördelar. Även om .resx-filer är lätta att läsa och underhålla under programutvecklingen ingår de sällan i färdiga program. Om de distribueras med ett program finns de som separata filer förutom det körbara programmet och dess tillhörande bibliotek. Däremot bäddas .resources-filer in i programmets körbara eller tillhörande sammansättningar. För lokaliserade program lägger dessutom beroendet av .resx-filer vid körning ansvaret för att hantera resursåterställning på utvecklaren. Om däremot en uppsättning satellitsammansättningar som innehåller inbäddade .resources-filer har skapats, hanterar den vanliga språkkörningen resursåterställningsprocessen.

Om du vill konvertera en .resx-fil till en .resources-fil använder du Resource File Generator (resgen.exe), som har följande grundläggande syntax:

 resgen.exe .resxFilename

Resultatet är en binär resursfil som har samma rotfilnamn som .resx-filen och filnamnstillägget .resources. Den här filen kan sedan kompileras till en körbar fil eller ett bibliotek vid kompileringstillfället. Om du använder Visual Basic-kompilatorn använder du följande syntax för att bädda in en .resources-fil i ett programs körbara fil:

vbc filename .vb -resource: .resourcesFilename

Om du använder C# är syntaxen följande:

 csc filename .cs -resource: .resourcesFilename

. resources-filen kan också bäddas in i en satellitsammansättning med hjälp av Assembly Linker (al.exe), som har följande grundläggande syntax:

al resourcesFilename -out: assemblyFilename

Se även