Programmgesteuertes Arbeiten mit RESX-Dateien

Hinweis

Dieser Artikel gilt für .NET Framework. Für .NET 5+ (einschließlich .NET Core) geltende Informationen finden Sie unter Ressourcen in RESX-Dateien.

Da XML-Ressourcendateien (RESX) aus ordnungsgemäß definiertem XML-Code bestehen müssen, einschließlich eines Headers, der einem bestimmten Schema entsprechen muss, und auf den Daten in Name/Wert-Paaren folgen, werden Sie vermutlich feststellen, dass das manuelle Erstellen dieser Dateien ziemlich fehlerträchtig ist. Alternativ können Sie RESX-Dateien programmgesteuert mithilfe von Typen und Elementen aus der .NET-Klassenbibliothek erstellen. Sie können die .NET-Klassenbibliothek auch zum Abrufen der in RESX-Dateien gespeicherten Ressourcen verwenden. In diesem Artikel wird erläutert, wie Sie die Typen und Member im Namespace System.Resources für das Arbeiten mit RESX-Dateien verwenden können.

In diesem Artikel wird das Arbeiten mit XML-Dateien (.resx) behandelt, die Ressourcen enthalten. Informationen zum Arbeiten mit binären Ressourcendateien, die in Assemblys eingebettet wurden, finden Sie unter ResourceManager.

Warnung

Es gibt nicht programmgesteuerte Verfahren zum Arbeiten mit RESX-Dateien. Wenn Sie einem Visual Studio-Projekt eine Ressourcendatei hinzufügen, stellt Visual Studio eine Oberfläche zum Erstellen und Warten einer RESX-Datei bereit und konvertiert die RESX-Datei zum Zeitpunkt der Kompilierung automatisch in eine RESOURCES-Datei. Ferner können Sie RESX-Dateien in einem Texteditor direkt bearbeiten. Um eine Beschädigung der Datei zu vermeiden, müssen Sie allerdings sorgfältig darauf achten, keine der in der Datei gespeicherten binären Informationen zu verändern.

Erstellen einer RESX-Datei

Sie können die Klasse System.Resources.ResXResourceWriter verwenden, um eine RESX-Datei programmgesteuert zu erstellen, indem Sie folgende Schritte ausführen:

  1. Instanziieren Sie ein ResXResourceWriter -Objekt, indem Sie die ResXResourceWriter(String) -Methode aufrufen und den Namen der RESX-Datei angeben. Der Dateiname muss die Erweiterung „.resx“ enthalten. Wenn Sie das ResXResourceWriter -Objekt in einem using -Block instanziieren, müssen Sie die Methode ResXResourceWriter.Close in Schritt 3 nicht explizit aufrufen.

  2. Rufen Sie die ResXResourceWriter.AddResource -Methode für jede Ressource auf, die Sie der Datei hinzufügen möchten. Verwenden Sie die Überladungen dieser Methode, um Zeichenfolgen-, Objekt- und binäre Daten (Bytearray) hinzuzufügen. Wenn es sich bei der Ressource um ein Objekt handelt, muss es serialisierbar sein.

  3. Rufen Sie die ResXResourceWriter.Close -Methode auf, um die Ressourcendatei zu generieren und alle Ressourcen freizugeben. Wenn das ResXResourceWriter -Objekt innerhalb eines using -Blocks erstellt wurde, werden Ressourcen in die RESX-Datei geschrieben, und die vom ResXResourceWriter -Objekt verwendeten Ressourcen werden am Ende des using -Blocks freigegeben.

Die resultierende RESX-Datei weist den entsprechenden Header und ein data -Kennzeichen für jede Ressource auf, die von der ResXResourceWriter.AddResource -Methode hinzugefügt wurde.

Warnung

Verwenden Sie Ressourcendateien nicht, um Kennwörter, sicherheitsrelevante Informationen oder private Daten zu speichern.

Im folgenden Beispiel wird eine RESX-Datei mit dem Namen "CarResources.resx" erstellt, mit der sechs Zeichenfolgen, ein Symbol und zwei anwendungsdefinierte Objekte (zwei Automobile -Objekte) gespeichert werden. Die Klasse Automobile, die im Beispiel definiert und instanziiert wird, ist mit dem Attribut SerializableAttribute markiert.

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

Tipp

Sie können auch Visual Studio zum Erstellen von RESX-Dateien verwenden. Zum Zeitpunkt der Kompilierung verwendet Visual Studio den Resource File Generator (Resgen.exe) , um die RESX-Datei in eine binäre Ressourcendatei (RESOURCES) zu konvertieren, und bettet sie entweder in eine Anwendungsassembly oder in eine Satellitenassembly ein.

Eine RESX-Datei kann nicht in eine von der Laufzeitumgebung ausführbare Datei eingebettet oder in eine Satellitenassembly kompiliert werden. Sie müssen die RESX-Datei mithilfe des Resource File Generator (Resgen.exe)in eine binäre Ressourcendatei (RESOURCES) konvertieren. Die resultierende RESOURCES-Datei kann dann in eine Anwendungsassembly oder eine Satellitenassembly eingebettet werden. Weitere Informationen finden Sie unter Erstellen von Ressourcendateien.

Aufzählen der Ressourcen

In bestimmten Fällen müssen anstelle einer bestimmten alle Ressourcen aus einer RESX-Datei abgerufen werden. Zu diesem Zweck können Sie die Klasse System.Resources.ResXResourceReader verwenden, die einen Enumerator für alle in der RESX-Datei enthaltenen Ressourcen enthält. Die System.Resources.ResXResourceReader -Klasse implementiert IDictionaryEnumerator, der ein DictionaryEntry -Objekt zurückgibt, das für jede Iteration der Schleife eine bestimmte Ressource darstellt. Seine DictionaryEntry.Key -Eigenschaft gibt den Schlüssel der Ressource und seine DictionaryEntry.Value -Eigenschaft den Wert der Ressource zurück.

Im folgenden Beispiel wird ein ResXResourceReader -Objekt für die im vorhergehenden Beispiel erstellte Datei „CarResources.resx“ erstellt, das durch die Ressourcendatei iteriert. Es fügt die zwei in der Ressourcendatei definierten Automobile -Objekte einem System.Collections.Generic.List<T> und fünf der sechs Zeichenfolgen einem SortedList -Objekt hinzu. Die Werte in dem SortedList -Objekt werden in ein Parameterarray konvertiert, das zum Anzeigen von Spaltenüberschriften in der Konsole verwendet wird. Die Werte der Eigenschaft Automobile werden ebenfalls in der Konsole angezeigt.

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

Abrufen einer bestimmten Ressource

Über das Aufzählen der Elemente in einer RESX-Datei hinaus können Sie mithilfe der System.Resources.ResXResourceSet -Klasse eine bestimmte Ressource anhand des Namens abrufen. Die ResourceSet.GetString(String) -Methode ruft den Wert einer benannten Zeichenfolgenressource ab. Die ResourceSet.GetObject(String) -Methode ruft den Wert eines benannten Objekts oder Binärdaten ab. Die Methode gibt ein Objekt zurück, für das anschließend eine Umwandlung (in C#) oder Konvertierung (in Visual Basic) in ein Objekt des geeigneten Typs erfolgen muss.

Im folgenden Beispiel wird die Überschriftenzeichenfolge eines Formulars und dessen Symbol anhand des Ressourcennamens abgerufen. Außerdem werden die von der Anwendung definierten Automobile-Objekte abgerufen, die im vorherigen Beispiel verwendet wurden, und in einem DataGridView-Steuerelement angezeigt.

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

Konvertieren von RESX-Dateien in binäre RESOURCES-Dateien

Das Konvertieren von RESX-Dateien in eingebettete binäre Ressourcendateien (RESOURCES-Dateien) bietet erhebliche Vorteile. Obwohl RESX-Dateien in der Entwicklungsphase einer Anwendung leicht zu lesen und zu verwalten sind, sind sie nur selten in den fertigen Anwendungen enthalten. Wenn sie als Teil einer Anwendung verteilt werden, liegen sie als separate Dateien vor, getrennt von der ausführbaren Programmdatei und ihren begleitenden Bibliotheken. Im Unterschied dazu sind RESOURCES-Dateien in die ausführbare Programmdatei oder ihre begleitenden Assemblys eingebettet. Außerdem wird beim Einsatz von RESX-Dateien zur Laufzeit bei lokalisierten Anwendungen der Entwickler für die Behandlung des Ressourcenfallbacks in die Pflicht genommen. Wenn demgegenüber eine Sammlung von Satellitenassemblys erstellt wurde, die eingebettete RESOURCES-Dateien enthalten, übernimmt die Common Language Runtime die Zuständigkeit für den Ressourcenfallbackvorgang.

Zum Konvertieren einer RESX-Datei in eine RESOURCES-Datei wird der Resource File Generator (Resgen.exe) verwendet, der die folgende grundlegende Syntax aufweist:

 resgen.exe .resxFilename

Das Ergebnis ist eine binäre Ressourcendatei, die den gleichen Stammdateinamen wie die RESX-Datei und darüber hinaus eine RESOURCES-Dateierweiterung aufweist. Diese Datei kann dann zum Zeitpunkt der Kompilierung in eine ausführbare Datei oder eine Bibliothek kompiliert werden. Wenn Sie den Visual Basic-Compiler verwenden, verwenden Sie die folgende Syntax, um eine RESOURCES-Datei in die ausführbare Programmdatei einer Anwendung einzubetten:

vbc filename .vb -resource: .resourcesFilename

Wenn Sie C# verwenden, lautet die Syntax wie folgt:

 csc filename .cs -resource: .resourcesFilename

Die RESOURCES-Datei kann mithilfe von Assembly Linker (al.exe) auch in eine Satellitenassembly eingebettet werden. Dafür gilt die folgende grundlegende Syntax:

al resourcesFilename -out: assemblyFilename

Weitere Informationen