Praca programistyczna z plikami .resx

Uwaga

Ten artykuł dotyczy programu .NET Framework. Aby uzyskać informacje dotyczące platformy .NET 5+ (w tym .NET Core), zobacz Zasoby w plikach resx.

Ponieważ pliki zasobów XML (resx) muszą składać się z dobrze zdefiniowanego kodu XML, w tym nagłówka, który musi postępować zgodnie z określonym schematem, po którym następują dane w parach nazw/wartości, może się okazać, że ręczne tworzenie tych plików jest podatne na błędy. Alternatywnie można programowo tworzyć pliki resx przy użyciu typów i elementów członkowskich w bibliotece klas platformy .NET. Bibliotekę klas platformy .NET można również użyć do pobierania zasobów przechowywanych w plikach resx. W tym artykule wyjaśniono, jak używać typów i elementów członkowskich w System.Resources przestrzeni nazw do pracy z plikami resx.

W tym artykule omówiono pracę z plikami XML (resx), które zawierają zasoby. Aby uzyskać informacje na temat pracy z plikami zasobów binarnych, które zostały osadzone w zestawach, zobacz ResourceManager.

Ostrzeżenie

Istnieją również sposoby pracy z plikami resx innymi niż programowo. Po dodaniu pliku zasobu do projektu programu Visual Studio program Visual Studio udostępnia interfejs do tworzenia i obsługi pliku resx oraz automatycznie konwertuje plik resx na plik resources w czasie kompilacji. Edytor tekstów umożliwia również bezpośrednie manipulowanie plikiem resx. Jednak aby uniknąć uszkodzenia pliku, należy zachować ostrożność, aby nie modyfikować żadnych informacji binarnych przechowywanych w pliku.

Tworzenie pliku resx

Możesz użyć System.Resources.ResXResourceWriter klasy , aby programowo utworzyć plik resx, wykonując następujące kroki:

  1. Utwórz wystąpienie obiektu, wywołując ResXResourceWriter(String) metodę ResXResourceWriter i podając nazwę pliku resx. Nazwa pliku musi zawierać rozszerzenie resx. Jeśli utworzysz wystąpienie ResXResourceWriter obiektu w using bloku, nie musisz jawnie wywoływać ResXResourceWriter.Close metody w kroku 3.

  2. Wywołaj metodę ResXResourceWriter.AddResource dla każdego zasobu, który chcesz dodać do pliku. Użyj przeciążeń tej metody, aby dodać dane ciągów, obiektów i binarnych (tablicy bajtów). Jeśli zasób jest obiektem, musi być serializowalny.

  3. Wywołaj metodę , ResXResourceWriter.Close aby wygenerować plik zasobu i zwolnić wszystkie zasoby. ResXResourceWriter Jeśli obiekt został utworzony w using bloku, zasoby są zapisywane w pliku resx, a zasoby używane przez ResXResourceWriter obiekt są zwalniane na końcu using bloku.

Wynikowy plik resx ma odpowiedni nagłówek i data tag dla każdego zasobu dodanego przez metodę ResXResourceWriter.AddResource .

Ostrzeżenie

Nie używaj plików zasobów do przechowywania haseł, informacji poufnych zabezpieczeń ani danych prywatnych.

Poniższy przykład tworzy plik resx o nazwie CarResources.resx, który przechowuje sześć ciągów, ikonę i dwa obiekty zdefiniowane przez aplikację (dwa Automobile obiekty). Klasa Automobile , która jest zdefiniowana i utworzona w przykładzie, jest oznaczona atrybutem 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

Napiwek

Program Visual Studio umożliwia również tworzenie plików resx. W czasie kompilacji program Visual Studio używa generatora plików zasobów (Resgen.exe), aby przekonwertować plik resx na plik zasobu binarnego (resources), a także osadza go w zestawie aplikacji lub zestawie satelitarnym.

Nie można osadzić pliku resx w pliku wykonywalnym środowiska uruchomieniowego ani skompilować go do zestawu satelitarnego. Plik resx należy przekonwertować na plik zasobu binarnego (.resources) przy użyciu generatora plików zasobów (Resgen.exe).. Wynikowy plik resources można następnie osadzać w zestawie aplikacji lub zestawie satelitarnym. Aby uzyskać więcej informacji, zobacz Tworzenie plików zasobów.

Wyliczanie zasobów

W niektórych przypadkach możesz pobrać wszystkie zasoby zamiast określonego zasobu z pliku resx. W tym celu można użyć System.Resources.ResXResourceReader klasy , która udostępnia moduł wyliczający dla wszystkich zasobów w pliku resx. Klasa System.Resources.ResXResourceReader implementuje IDictionaryEnumeratorobiekt , który zwraca DictionaryEntry obiekt reprezentujący określony zasób dla każdej iteracji pętli. Jego DictionaryEntry.Key właściwość zwraca klucz zasobu, a jego DictionaryEntry.Value właściwość zwraca wartość zasobu.

Poniższy przykład tworzy ResXResourceReader obiekt dla pliku CarResources.resx utworzonego w poprzednim przykładzie i wykonuje iterację za pośrednictwem pliku zasobu. Dodaje dwa Automobile obiekty zdefiniowane w pliku zasobów do System.Collections.Generic.List<T> obiektu i dodaje pięć z sześciu ciągów do SortedList obiektu. Wartości w SortedList obiekcie są konwertowane na tablicę parametrów, która służy do wyświetlania nagłówków kolumn w konsoli. Wartości Automobile właściwości są również wyświetlane w konsoli programu .

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

Pobieranie określonego zasobu

Oprócz wyliczania elementów w pliku resx można pobrać określony zasób według nazwy przy użyciu System.Resources.ResXResourceSet klasy . Metoda ResourceSet.GetString(String) pobiera wartość nazwanego zasobu ciągu. Metoda ResourceSet.GetObject(String) pobiera wartość nazwanego obiektu lub danych binarnych. Metoda zwraca obiekt, który musi zostać rzutowany (w języku C#) lub przekonwertowany (w Visual Basic) na obiekt odpowiedniego typu.

Poniższy przykład pobiera ciąg podpis formularza i ikonę według ich nazw zasobów. Pobiera również obiekty zdefiniowane przez Automobile aplikację używane w poprzednim przykładzie i wyświetla je w kontrolce 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

Konwertowanie plików resx na pliki binarne .resources

Konwertowanie plików resx na pliki osadzonego zasobu binarnego (.resources) ma znaczne zalety. Chociaż pliki resx są łatwe do odczytania i konserwacji podczas tworzenia aplikacji, rzadko są dołączane do gotowych aplikacji. Jeśli są one dystrybuowane z aplikacją, istnieją one jako oddzielne pliki oprócz pliku wykonywalnego aplikacji i towarzyszących jej bibliotek. Z kolei pliki resources są osadzone w pliku wykonywalny aplikacji lub towarzyszących im zestawach. Ponadto w przypadku zlokalizowanych aplikacji poleganie na plikach resx w czasie wykonywania nakłada na dewelopera odpowiedzialność za obsługę rezerwowego zasobu. Natomiast jeśli został utworzony zestaw zestawów satelickich zawierających osadzone pliki resources, środowisko uruchomieniowe języka wspólnego obsługuje proces rezerwowania zasobów.

Aby przekonwertować plik resx na plik resources , należy użyć generatora plików zasobów (resgen.exe), który ma następującą podstawową składnię:

 resgen.exe .resxFilename

Wynikiem jest binarny plik zasobu, który ma taką samą nazwę pliku głównego jak plik resx i rozszerzenie pliku resources. Ten plik można następnie skompilować do pliku wykonywalnego lub biblioteki w czasie kompilacji. Jeśli używasz kompilatora języka Visual Basic, użyj następującej składni, aby osadzić plik resources w pliku wykonywalnym aplikacji:

vbc filename .vb -resource: .resourcesFilename

Jeśli używasz języka C#, składnia jest następująca:

 csc filename .cs -resource: .resourcesFilename

Plik resources można również osadzać w zestawie satelitarnym przy użyciu konsolidatora zestawów (al.exe), który ma następującą podstawową składnię:

al resourcesFilename -out: assemblyFilename

Zobacz też