在C#中修改.resx文件

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

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

如果要保留资源文件中的现有注释,请使用此(基于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(); } 
  public static void AddOrUpdateResource(string key, string value) { var resx = new List(); using (var reader = new ResXResourceReader(resourceFilepath)) { resx = reader.Cast().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(); } } 

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

这是我的版本基于Ers’并基于SirMoreno的代码。 只是短一点。 这仍然不是对metaData的处理,这对我来说是可能的但不是必需的。

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

这是改进Womp的答案 – 没有过时的Hashtable,检查文件是否存在并使用LINQ:

  public static void UpdateResourceFile(Dictionary data, string path) { Dictionary resourceEntries = new Dictionary(); if (File.Exists(path)) { //Get existing resources ResXResourceReader reader = new ResXResourceReader(path); resourceEntries = reader.Cast().ToDictionary(d => d.Key.ToString(), d => d.Value?.ToString() ?? ""); reader.Close(); } //Modify resources here... foreach (KeyValuePair 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 entry in resourceEntries) { resourceWriter.AddResource(entry.Key, resourceEntries[entry.Key]); } resourceWriter.Generate(); resourceWriter.Close(); } 

所有其他答案都使用ResXResourceWriter,但对于某些特殊情况,简单地将Resources.resx文件作为XML文档使用可能是可行的。

我有一个特定的情况,我想操作一组图标文件的Resources.resx条目。 可能有多达几百个条目,我可以指望它们看起来完全像这样:

   ..\..\..\..\..\..\Icons\Incors-workplace2-16x16.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a  

我尝试将ResXResourceWriter和ResXResourceReader用于此程序,但最终我将各种Resources.resx文件作为XML文档打开并以这种方式操作它们。 整个程序太大了(也是特定于应用程序)在这里发布,但我会发布一些代码来展示一些可以使用的技术。

  ///  /// Method to load a Resources.resx file (if it exists) as an XML Document object. ///  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; } // --------------------------------------------------------------------------- ///  /// Method to fix the names of any resources that contain '-' instead of '_'. ///  private static void FixResourceNames(XmlDocument xdResx, ref bool resxModified) { // Loop for all of the  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"); // --------------------------------------------------------------------------- ///  /// 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. ///  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; } // --------------------------------------------------------------------------- ///  /// Method to filter out non-icon resources and return the resource name for the icon-based /// resource in the Resources.resx object. ///  /// name of resource, ie, name= value, or null if not icon resource 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;