Delen via


Programmatisch werken met .resx-bestanden

Notitie

Dit artikel is van toepassing op .NET Framework. Zie Resources in .resx-bestanden voor informatie die van toepassing is op .NET 5+ (inclusief .NET Core).

Omdat XML-resourcebestanden (.resx) moeten bestaan uit goed gedefinieerde XML, inclusief een header die een specifiek schema moet volgen, gevolgd door gegevens in naam-/waardeparen, kan het zijn dat het handmatig maken van deze bestanden foutgevoelig is. Als alternatief kunt u programmatisch .resx-bestanden maken met behulp van typen en leden in de .NET-klassebibliotheek. U kunt ook de .NET-klassebibliotheek gebruiken om resources op te halen die zijn opgeslagen in RESX-bestanden. In dit artikel wordt uitgelegd hoe u de typen en leden in de System.Resources naamruimte kunt gebruiken om te werken met .resx-bestanden.

In dit artikel wordt beschreven hoe u werkt met XML-bestanden (.resx) die resources bevatten. Zie voor meer informatie over het werken met binaire resourcebestanden die zijn ingesloten in assembly's ResourceManager.

Waarschuwing

Er zijn ook manieren om te werken met .resx-bestanden anders dan programmatisch. Wanneer u een resourcebestand toevoegt aan een Visual Studio-project , biedt Visual Studio een interface voor het maken en onderhouden van een RESX-bestand en converteert het RESX-bestand automatisch naar een .resources-bestand tijdens het compileren. U kunt ook een teksteditor gebruiken om een RESX-bestand rechtstreeks te bewerken. Als u echter wilt voorkomen dat het bestand beschadigd raakt, moet u ervoor zorgen dat u geen binaire informatie wijzigt die in het bestand is opgeslagen.

Een RESX-bestand maken

U kunt de System.Resources.ResXResourceWriter klasse gebruiken om programmatisch een RESX-bestand te maken door de volgende stappen uit te voeren:

  1. Instantieer een ResXResourceWriter object door de methode aan ResXResourceWriter(String) te roepen en de naam van het RESX-bestand op te leveren. De bestandsnaam moet de extensie .resx bevatten. Als u het ResXResourceWriter object in een using blok instantiëren, hoeft u de ResXResourceWriter.Close methode niet expliciet aan te roepen in stap 3.

  2. Roep de ResXResourceWriter.AddResource methode aan voor elke resource die u aan het bestand wilt toevoegen. Gebruik de overbelasting van deze methode om tekenreeks-, object- en binaire gegevens (bytematrix) toe te voegen. Als de resource een object is, moet deze serialiseerbaar zijn.

  3. Roep de ResXResourceWriter.Close methode aan om het resourcebestand te genereren en alle resources vrij te geven. Als het ResXResourceWriter object in een using blok is gemaakt, worden resources naar het RESX-bestand geschreven en worden de resources die door het ResXResourceWriter object worden gebruikt, aan het einde van het using blok vrijgegeven.

Het resulterende RESX-bestand bevat de juiste header en een data tag voor elke resource die door de ResXResourceWriter.AddResource methode wordt toegevoegd.

Waarschuwing

Gebruik geen resourcebestanden om wachtwoorden, beveiligingsgevoelige informatie of persoonlijke gegevens op te slaan.

In het volgende voorbeeld wordt een RESX-bestand gemaakt met de naam CarResources.resx waarin zes tekenreeksen, een pictogram en twee toepassingsgedefinieerde objecten (twee Automobile objecten) worden opgeslagen. De Automobile klasse, die is gedefinieerd en geïnstantieerd in het voorbeeld, wordt gelabeld met het SerializableAttribute kenmerk.

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

Tip

U kunt Visual Studio ook gebruiken om RESX-bestanden te maken. Tijdens het compileren gebruikt Visual Studio de Resource File Generator (Resgen.exe) om het RESX-bestand te converteren naar een binair resourcebestand (.resources) en wordt het ook ingesloten in een toepassingsassembly of een satellietassembly.

U kunt een RESX-bestand niet insluiten in een uitvoerbaar runtimebestand of het in een satellietassembly compileren. U moet uw RESX-bestand converteren naar een binair resourcebestand (.resources) met behulp van de resourcebestandgenerator (Resgen.exe).) Het resulterende .resources-bestand kan vervolgens worden ingesloten in een toepassingsassembly of een satellietassembly. Zie Resourcebestanden maken voor meer informatie.

Resources opsommen

In sommige gevallen wilt u mogelijk alle resources ophalen, in plaats van een specifieke resource, uit een RESX-bestand. Hiervoor kunt u de System.Resources.ResXResourceReader klasse gebruiken, die een opsomming biedt voor alle resources in het RESX-bestand. De System.Resources.ResXResourceReader klasse implementeert IDictionaryEnumerator, die een DictionaryEntry object retourneert dat een bepaalde resource vertegenwoordigt voor elke iteratie van de lus. De DictionaryEntry.Key eigenschap retourneert de sleutel van de resource en de DictionaryEntry.Value eigenschap retourneert de waarde van de resource.

In het volgende voorbeeld wordt een ResXResourceReader object gemaakt voor het CarResources.resx-bestand dat in het vorige voorbeeld is gemaakt en doorloopt het resourcebestand. Hiermee worden de twee Automobile objecten die in het resourcebestand zijn gedefinieerd, toegevoegd aan een System.Collections.Generic.List<T> object en worden vijf van de zes tekenreeksen aan een SortedList object toegevoegd. De waarden in het SortedList object worden geconverteerd naar een parametermatrix, die wordt gebruikt om kolomkoppen weer te geven naar de console. De Automobile eigenschapswaarden worden ook weergegeven in de 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

Een specifieke resource ophalen

Naast het inventariseren van de items in een RESX-bestand, kunt u een specifieke resource op naam ophalen met behulp van de System.Resources.ResXResourceSet klasse. Met ResourceSet.GetString(String) de methode wordt de waarde van een benoemde tekenreeksresource opgehaald. De ResourceSet.GetObject(String) methode haalt de waarde van een benoemd object of binaire gegevens op. De methode retourneert een object dat vervolgens moet worden gecast (in C#) of moet worden geconverteerd (in Visual Basic) naar een object van het juiste type.

In het volgende voorbeeld wordt de onderschrift tekenreeks en het pictogram van een formulier opgehaald op basis van de resourcenamen. Ook worden de toepassingsgedefinieerde objecten opgehaald Automobile die in het vorige voorbeeld worden gebruikt en weergegeven in een DataGridView besturingselement.

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

.resx-bestanden converteren naar binaire .resources-bestanden

Het converteren van .resx-bestanden naar ingesloten binaire bronbestanden (.resources) heeft aanzienlijke voordelen. Hoewel .resx-bestanden gemakkelijk te lezen en te onderhouden zijn tijdens het ontwikkelen van toepassingen, worden ze zelden opgenomen in voltooide toepassingen. Als ze worden gedistribueerd met een toepassing, bestaan ze als afzonderlijke bestanden, afgezien van het uitvoerbare bestand van de toepassing en de bijbehorende bibliotheken. Resources-bestanden worden daarentegen ingesloten in het uitvoerbare bestand van de toepassing of de bijbehorende assembly's. Daarnaast is voor gelokaliseerde toepassingen, afhankelijk van .resx-bestanden tijdens runtime, de verantwoordelijkheid voor het afhandelen van resourceval op de ontwikkelaar. Als daarentegen een set satellietassembly's met ingesloten .resources-bestanden is gemaakt, verwerkt de algemene taalruntime het terugvalproces van de resource.

Als u een RESX-bestand wilt converteren naar een .resources-bestand , gebruikt u resourcebestandgenerator (resgen.exe) met de volgende basissyntaxis:

 resgen.exe .resxFilename

Het resultaat is een binair bronbestand met dezelfde hoofdbestandsnaam als het RESX-bestand en een .resources-bestandsextensie. Dit bestand kan vervolgens worden gecompileerd in een uitvoerbaar bestand of een bibliotheek tijdens het compileren. Als u de Visual Basic-compiler gebruikt, gebruikt u de volgende syntaxis om een .resources-bestand in te sluiten in het uitvoerbare bestand van een toepassing:

vbc filename .vb -resource: .resourcesFilename

Als u C# gebruikt, is de syntaxis als volgt:

 csc filename .cs -resource: .resourcesFilename

Het .resources-bestand kan ook worden ingesloten in een satellietassembly met behulp van Assembly Linker (al.exe), met de volgende basissyntaxis:

al resourcesFilename -out: assemblyFilename

Zie ook