私は.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();
}
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();
}
}
リソースファイルに既存のコメントを保持したい場合は、これを使用してください(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();
}
Wompはそれを正しく理解しました(10倍)。
しかし、これは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();
}
これは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();
}
他のすべての回答は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;
これは、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;
}