2009-03-24 30 views
21

Tenía un archivo .resx que contiene pares de valores de nombre de cadena. Ahora quiero modificar los valores en ciertos pares de valores de nombre programáticamente usando C#. ¿Cómo puedo lograr eso?Modificación del archivo .resx en C#

Gracias de antemano.

+0

Me gustaría saber qué caso (s) de uso. Tengo un cliente que desea modificar los recursos sobre la marcha, pero todo esto parece antinatural y contrario a los propósitos de los archivos de recursos. – MrBoJangles

Respuesta

25

Hay un espacio de nombre completo para la gestión de recursos: System.Resources. Consulte la clase ResourceManager, así como ResXResourceReader y ResXResourceWriter.

http://msdn.microsoft.com/en-us/library/system.resources.aspx


me las arreglé para poner mis manos en un método de depuración muy antiguo que solía usar en un momento cuando me estaba probando algunas cosas relacionadas con los recursos. Esto debería hacer el truco para ti.

public static void UpdateResourceFile(Hashtable data, String path) 
    { 
     Hashtable resourceEntries = new Hashtable(); 

     //Get existing resources 
     ResXResourceReader reader = new ResXResourceReader(path); 
     if (reader != null) 
     { 
      IDictionaryEnumerator id = reader.GetEnumerator(); 
      foreach (DictionaryEntry d in reader) 
      { 
       if (d.Value == null) 
        resourceEntries.Add(d.Key.ToString(), ""); 
       else 
        resourceEntries.Add(d.Key.ToString(), d.Value.ToString()); 
      } 
      reader.Close(); 
     } 

     //Modify resources here... 
     foreach (String key in data.Keys) 
     { 
      if (!resourceEntries.ContainsKey(key)) 
      { 

       String value = data[key].ToString(); 
       if (value == null) value = ""; 

       resourceEntries.Add(key, value); 
      } 
     } 

     //Write the combined resource file 
      ResXResourceWriter resourceWriter = new ResXResourceWriter(path); 

      foreach (String key in resourceEntries.Keys) 
      { 
       resourceWriter.AddResource(key, resourceEntries[key]); 
      } 
      resourceWriter.Generate(); 
      resourceWriter.Close(); 

    } 
+1

¿Es esta la única forma de ACTUALIZAR un kvp en un archivo resx? Parece que simplemente agregaría otro kvp al archivo. – dotnetN00b

+0

El problema es que perderá los comentarios de recursos si eso es importante para usted. – uli78

4

Womp lo hizo bien (10x).

Pero aquí hay un código que mantiene el orden de archivos XML, agregue nuevos al final del archivo. (para control de código fuente)

//Need dll System.Windows.Forms 
    public static void UpdateResourceFile(Hashtable data, String path) 
    { 
     Hashtable resourceEntries = new Hashtable(); 

     //Get existing resources 
     ResXResourceReader reader = new ResXResourceReader(path); 
     ResXResourceWriter resourceWriter = new ResXResourceWriter(path); 

     if (reader != null) 
     { 
      IDictionaryEnumerator id = reader.GetEnumerator(); 
      foreach (DictionaryEntry d in reader) 
      { 
       //Read from file: 
       string val = ""; 
       if (d.Value == null) 
        resourceEntries.Add(d.Key.ToString(), ""); 
       else 
       { 
        resourceEntries.Add(d.Key.ToString(), d.Value.ToString()); 
        val = d.Value.ToString(); 
       } 

       //Write (with read to keep xml file order) 
       resourceWriter.AddResource(d.Key.ToString(), val); 

      } 
      reader.Close(); 
     } 

     //Add new data (at the end of the file): 
     Hashtable newRes = new Hashtable(); 
     foreach (String key in data.Keys) 
     { 
      if (!resourceEntries.ContainsKey(key)) 
      { 

       String value = data[key].ToString(); 
       if (value == null) value = ""; 

       resourceWriter.AddResource(key, value); 
      } 
     } 

     //Write to file 
     resourceWriter.Generate(); 
     resourceWriter.Close(); 

    } 
9

Si desea mantener los comentarios existentes en los archivos de recursos a continuación, utilizar este (basado en el código de SirMoreno modificado)

public static void UpdateResourceFile(Hashtable data, String path) 
    { 
     Hashtable resourceEntries = new Hashtable(); 

     //Get existing resources 
     ResXResourceReader reader = new ResXResourceReader(path); 
     reader.UseResXDataNodes = true; 
     ResXResourceWriter resourceWriter = new ResXResourceWriter(path); 
     System.ComponentModel.Design.ITypeResolutionService typeres = null; 
     if (reader != null) 
     { 
      IDictionaryEnumerator id = reader.GetEnumerator(); 
      foreach (DictionaryEntry d in reader) 
      { 
       //Read from file: 
       string val = ""; 
       if (d.Value == null) 
        resourceEntries.Add(d.Key.ToString(), ""); 
       else 
       { 
        val = ((ResXDataNode)d.Value).GetValue(typeres).ToString(); 
        resourceEntries.Add(d.Key.ToString(), val); 

       } 

       //Write (with read to keep xml file order) 
       ResXDataNode dataNode = (ResXDataNode)d.Value; 

       //resourceWriter.AddResource(d.Key.ToString(), val); 
       resourceWriter.AddResource(dataNode); 

      } 
      reader.Close(); 
     } 

     //Add new data (at the end of the file): 
     Hashtable newRes = new Hashtable(); 
     foreach (String key in data.Keys) 
     { 
      if (!resourceEntries.ContainsKey(key)) 
      { 

       String value = data[key].ToString(); 
       if (value == null) value = ""; 

       resourceWriter.AddResource(key, value); 
      } 
     } 

     //Write to file 
     resourceWriter.Generate(); 
     resourceWriter.Close(); 

    } 
9
public static void AddOrUpdateResource(string key, string value) 
    { 
     var resx = new List<DictionaryEntry>(); 
     using (var reader = new ResXResourceReader(resourceFilepath)) 
     { 
      resx = reader.Cast<DictionaryEntry>().ToList(); 
      var existingResource = resx.Where(r => r.Key.ToString() == key).FirstOrDefault(); 
      if (existingResource.Key == null && existingResource.Value == null) // NEW! 
      { 
       resx.Add(new DictionaryEntry() { Key = key, Value = value }); 
      } 
      else // MODIFIED RESOURCE! 
      { 
       var modifiedResx = new DictionaryEntry() 
        { Key = existingResource.Key, Value = value }; 
       resx.Remove(existingResource); // REMOVING RESOURCE! 
       resx.Add(modifiedResx); // AND THEN ADDING RESOURCE! 
      } 
     } 
     using (var writer = new ResXResourceWriter(ResxPathEn)) 
     { 
      resx.ForEach(r => 
         { 
          // Again Adding all resource to generate with final items 
          writer.AddResource(r.Key.ToString(), r.Value.ToString()); 
         }); 
      writer.Generate(); 
     } 
    } 
+1

Realy Usefull Thanks Alot :) +1 Deseo que soy el dueño de esta pregunta. Marque la suya es la correcta :)) gracias de nuevo – Marwan

+0

Muy útil, Muchísimas gracias –

0

Esta es mi versión basada en Ers 'y basado en el código de SirMoreno. Solo un poco más corto. Esto todavía no está manejando metaData que es posible pero no necesario para mí.

public static bool AddToResourceFile(string key, string value, string comment, string path) 
    { 
     using (ResXResourceWriter resourceWriter = new ResXResourceWriter(path)) 
     { 
      //Get existing resources 
      using (ResXResourceReader reader = new ResXResourceReader(path) { UseResXDataNodes = true }) 
      { 
       foreach (DictionaryEntry resEntry in reader) 
       { 
        ResXDataNode node = resEntry.Value as ResXDataNode; 
        if (node == null) continue; 

        if (string.CompareOrdinal(key, node.Name) == 0) 
        { 
         // Keep resources untouched. Alternativly modify this resource. 
         return false; 
        } 

        resourceWriter.AddResource(node); 
       } 
      } 

      //Add new data (at the end of the file): 
      resourceWriter.AddResource(new ResXDataNode(key, value) { Comment = comment }); 

      //Write to file 
      resourceWriter.Generate(); 
     } 
     return true; 
    } 
0

Esto se mejora la respuesta de Womp - sin obsoleta Hashtable, comprobando si existe el archivo y el uso de LINQ:

public static void UpdateResourceFile(Dictionary<string, string> data, string path) 
    { 
     Dictionary<string, string> resourceEntries = new Dictionary<string, string>(); 
     if (File.Exists(path)) 
     { 
      //Get existing resources 
      ResXResourceReader reader = new ResXResourceReader(path); 
      resourceEntries = reader.Cast<DictionaryEntry>().ToDictionary(d => d.Key.ToString(), d => d.Value?.ToString() ?? ""); 
      reader.Close(); 
     } 

     //Modify resources here... 
     foreach (KeyValuePair<string, string> entry in data) 
     { 
      if (!resourceEntries.ContainsKey(entry.Key)) 
      { 
       if (!resourceEntries.ContainsValue(entry.Value)) 
       { 
        resourceEntries.Add(entry.Key, entry.Value); 
       } 
      } 
     } 

     string directoryPath = Path.GetDirectoryName(path); 
     if (!string.IsNullOrEmpty(directoryPath)) 
     { 
      Directory.CreateDirectory(directoryPath); 
     } 

     //Write the combined resource file 
     ResXResourceWriter resourceWriter = new ResXResourceWriter(path); 
     foreach (KeyValuePair<string, string> entry in resourceEntries) 
     { 
      resourceWriter.AddResource(entry.Key, resourceEntries[entry.Key]); 
     } 
     resourceWriter.Generate(); 
     resourceWriter.Close(); 
    } 
0

Todas las otras respuestas hacen uso de ResXResourceWriter, pero para algunos casos especiales puede ser Es posible y mejor simplemente trabajar con el archivo Resources.resx como un documento XML.

Tengo una situación específica en la que deseo manipular las entradas de Resources.resx para un conjunto de archivos de iconos. Puede haber hasta varios cientos de entradas, y puedo contar con ellos todo se ve exactamente como esto:

<data name="Incors_workplace2_16x16" type="System.Resources.ResXFileRef, System.Windows.Forms"> 
    <value>..\..\..\..\..\..\Icons\Incors-workplace2-16x16.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> 
    </data> 

He intentado utilizar ResXResourceWriter y ResXResourceReader para este programa, pero terminé en lugar de abrir los diversos archivos Resources.resx como documentos XML y manipularlos de esa manera. Todo el programa es demasiado extenso (y también específico de la aplicación) para publicarlo aquí, pero publicaré algunos tocones de código para mostrar algunas de las técnicas que se pueden usar.

 /// <summary> 
     /// Method to load a Resources.resx file (if it exists) as an XML Document object. 
     /// </summary> 
     private static XmlDocument LoadResourcesResx(string projectPath) 
     { 
     string fileName = projectPath + @"Properties\Resources.resx"; 
     if (!File.Exists(fileName)) 
      return null; 

     XmlDocument xdResx = new XmlDocument(); 
     xdResx.Load(fileName); 
     return xdResx; 
     } 

    // --------------------------------------------------------------------------- 


     /// <summary> 
     /// Method to fix the names of any resources that contain '-' instead of '_'. 
     /// </summary> 
     private static void FixResourceNames(XmlDocument xdResx, ref bool resxModified) 
     { 
     // Loop for all of the <data> elements that have name= attributes (node = "name" attr.) 
     XmlNodeList xnlDataElements = xdResx.SelectNodes("/root/data/@name"); 
     if (xnlDataElements != null) 
     { 
      foreach (XmlNode xmlNode in xnlDataElements) 
      { 
       // Modify the name= attribute if necessary 
       string oldDataName = xmlNode.Value; 
       string newDataName = oldDataName.Replace('-', '_'); 
       if (oldDataName != newDataName) 
       { 
        xmlNode.Value = newDataName; 
        resxModified = true; 
       } 
      } 
     } 
     } 

    // --------------------------------------------------------------------------- 

      // Prepare to add resource nodes to client-basic's Resources.resx file 
      XmlNode rootNodeBasic = xdResx.SelectSingleNode("/root"); 
    // --------------------------------------------------------------------------- 

     /// <summary> 
     /// Sub-method of above method (not included here) to copy a new icon usage from one of the client-maxi projects 
     /// to the client-basic project. 
     /// </summary> 
     private static bool CopyIconToClientBasic(string projectPath, XmlDocument xdResxBasic, 
               XmlNode rootNodeBasic, XmlNode xmlNodeMaxi) 
     { 
     // Check if this is an icon-based resource, and get the resource name if so 
     string oldDataName = GetAndCheckResourceName(xmlNodeMaxi); 
     if (oldDataName == null) 
      return false; 

     // Determine if there is a 16x16, 20x20, 24x24, 32x32 or 48x48 version of this icon 
     // available, picking the lowest size to reduce client-basic assembly increase for a 
     // resource which will probably never be used 
     string oldFileName = xmlNodeMaxi.FirstChild.InnerText.Split(';')[0]; 
     string minSize = FindMinimumIconSize(projectPath, oldFileName); // Not included here 
     if (minSize == null) 
      return false; // Something wrong, can't find icon file 

     // Test if client-basic's resources includes a version of this icon for smallest size 
     string newDataName = oldDataName.Remove(oldDataName.Length - 5) + minSize; 
     if (xdResxBasic.SelectSingleNode("/root/data[@name='" + newDataName + "']") != null) 
      return false; // Already in client-basic 

     // Add the smallest available size version of this icon to the client-basic project 
     string oldSize = oldDataName.Substring(oldDataName.Length - 5); // "16x16", "20x20" 
     XmlNode newNodeBasic = xdResxBasic.ImportNode(xmlNodeMaxi, true); 
     if (newNodeBasic.Attributes != null) 
      newNodeBasic.Attributes["name"].Value = newDataName; // Maybe force smaller size 
     newNodeBasic.FirstChild.InnerText = 
            newNodeBasic.FirstChild.InnerText.Replace(oldSize, minSize); 
     rootNodeBasic.AppendChild(newNodeBasic); 
     return true; 
     } 

    // --------------------------------------------------------------------------- 

     /// <summary> 
     /// Method to filter out non-icon resources and return the resource name for the icon-based 
     /// resource in the Resources.resx object. 
     /// </summary> 
     /// <returns>name of resource, i.e., name= value, or null if not icon resource</returns> 
     private static string GetAndCheckResourceName(XmlNode xmlNode) 
     { 
     // Ignore resources that aren't PNG-based icon files with a standard size. This 
     // includes ignoring ICO-based resources. 
     if (!xmlNode.FirstChild.InnerText.Contains(";System.Drawing.Bitmap,")) 
      return null; 
     if (xmlNode.Attributes == null) 
      return null; 

     string dataName = xmlNode.Attributes["name"].Value; 

     if (dataName.EndsWith("_16x16", StringComparison.Ordinal) || 
      dataName.EndsWith("_20x20", StringComparison.Ordinal) || 
      dataName.EndsWith("_24x24", StringComparison.Ordinal) || 
      dataName.EndsWith("_32x32", StringComparison.Ordinal) || 
      dataName.EndsWith("_48x48", StringComparison.Ordinal)) 
      return dataName; 

     return null; 
     } 

    // --------------------------------------------------------------------------- 

     // It's too messy to create a new node from scratch when not using the ResXResourceWriter 
     // facility, so we cheat and clone an existing icon entry, the one for Cancel buttons 

     // Get the Cancel icon name and filename 
     BuiltInIcon cancelIcon = BuiltInIconNames.FindIconByName(BuiltInIconNames.CCancel); 
     string cancelIconResourceName = cancelIcon.ResourceName + "_16x16"; 

     // Find it in the Resources.resx file - it should be there 
     XmlNode cancelIconNode = 
       xdResxBasic.SelectSingleNode("/root/data[@name='" + cancelIconResourceName + "']"); 
     if (cancelIconNode == null) 
     { 
      PreprocessorMain.DisplayError(0x27b699fu, "Icon " + cancelIconResourceName + 
                 " not found in Resources.resx file."); 
      return false; 
     } 

     // Make a clone of this node in the Resources.resx file 
     XmlNode newNode = cancelIconNode.Clone(); 
     if (newNode.Attributes == null) // Not possible? 
     { 
      PreprocessorMain.DisplayError(0x27b8038u, "Node for icon " + cancelIconResourceName + 
                 " not as expected in Resources.resx file."); 
      return false; 
     } 

     // Modify the cloned XML node to represent the desired icon file/resource and add it to the 
     // Resources.resx file 
     newNode.Attributes["name"].Value = iconResourceName; 
     newNode.InnerText = 
        newNode.InnerText.Replace(cancelIcon.FileNameNoSize + "-16x16.png", iconFileName); 
     rootNodeBasic.AppendChild(newNode); 
     resxModified = true; 
Cuestiones relacionadas