在C#中修改.resx文件

时间:2009-03-24 06:15:41

标签: c# resx

我有一个.resx文件,其中包含名称 - 值对(两个字符串)。现在我想使用C#以编程方式修改某些名称 - 值对中的值。我怎样才能做到这一点?

8 个答案:

答案 0 :(得分:25)

资源管理有一个完整的命名空间: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();

    }

答案 1 :(得分:9)

如果你想保留资源文件中的现有注释,那么使用它(基于SirMoreno的代码修改)

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

    }

答案 2 :(得分: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();
        }
    }

答案 3 :(得分:4)

Womp说得对(10x)。

但这是一个保持XML文件顺序的代码,在文件末尾添加new。 (用于源代码管理)

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

    }

答案 4 :(得分:0)

这是我的版本基于Ers&#39;并基于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;
    }

答案 5 :(得分:0)

这是改进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();
    }

答案 6 :(得分:0)

所有其他答案都使用了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;

答案 7 :(得分:0)

在 Visual Studio 中,转到资源管理器窗口,然后您将看到其顶部的菜单“访问修饰符”将其更改为公开并保存。会更新的