Изменение файла .resx в C # - PullRequest
22 голосов
/ 24 марта 2009

У меня есть файл .resx, который содержит пары имя-значение (обе строки). Теперь я хочу изменить значения в определенных парах имя-значение программно, используя C #. Как мне этого добиться?

Ответы [ 7 ]

25 голосов
/ 24 марта 2009

Существует целое пространство имен для управления ресурсами: System.Resources. Изучите класс ResourceManager, а также ResXResourceReader и ResXResourceWriter.

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


Мне удалось взять в руки очень старый метод отладки, который я использовал в какой-то момент, когда тестировал некоторые вещи, связанные с ресурсами. Это должно помочь вам.

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();

    }
9 голосов
/ 15 августа 2015
    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();
        }
    }
9 голосов
/ 29 марта 2013

Если вы хотите сохранить существующие комментарии в файлах ресурсов, используйте это (на основе измененного кода Сирморено)

 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();

    }
4 голосов
/ 25 июля 2012

Womp правильно понял (10x).

Но вот код, который поддерживает порядок файлов XML, добавьте новый в конце файла. (для контроля источника)

    //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();

    }
0 голосов
/ 16 апреля 2017

Все остальные ответы используют ResXResourceWriter, но для определенных особых случаев может быть целесообразно и лучше просто работать с файлом Resources.resx в качестве XML-документа.

У меня есть особая ситуация, когда я хочу манипулировать записями Resources.resx для набора файлов значков. Может быть до нескольких сотен записей, и я могу рассчитывать на то, что все они будут выглядеть примерно так:

  <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>

Я попытался использовать ResXResourceWriter и ResXResourceReader для этой программы, но в итоге я вместо этого открыл различные файлы Resources.resx в виде документов XML и манипулировал ими таким образом. Вся программа слишком велика (и слишком специфична для конкретного приложения), чтобы публиковать ее здесь, но я опубликую несколько фрагментов кода, чтобы показать некоторые методы, которые можно использовать.

      /// <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;
0 голосов
/ 09 ноября 2016

Это улучшенный ответ Womp - без устаревшей Hashtable, проверка наличия файла и использование 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 голосов
/ 21 октября 2016

Это моя версия, основанная на Ers 'и основанная на коде SirMoreno. Просто немного короче. Это все еще не метаданные, которые возможны, но не являются необходимыми для меня.

    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;
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...