I know this is kind of obsessive, but is there a way to control the order that the TagBuilder
class renders the attributes of an HTML tag when you call ToString()
?
ie so that
var tb = new TagBuilder("meta");
tb.Attributes.Add("http-equiv", "Content-Type");
tb.Attributes.Add("content", "text/html; charset=utf-8");
tb.ToString(TagRenderMode.SelfClosing)
will return
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
not
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
Changing the order that you add the attributes doesn't change it, it seems to be rendering in alphabetical order
Try using this class, which inherits the TagBuilder and overrides the ToString method, building a SortedDictionary from the Attributes and using that dictionary to render.
public class MyTagBuilder : TagBuilder
{
//required to inherit from TagBuilder
public MyTagBuilder(string tagName) : base(tagName){}
//new hides the original ToString(TagRenderMode renderMode)
//The only changes in this method is that all calls to GetAttributesString
//have been changed to GetMyAttributesString
public new string ToString(TagRenderMode renderMode)
{
switch (renderMode)
{
case TagRenderMode.StartTag:
return string.Format(CultureInfo.InvariantCulture, "<{0}{1}>", new object[] { this.TagName, this.GetMyAttributesString() });
case TagRenderMode.EndTag:
return string.Format(CultureInfo.InvariantCulture, "</{0}>", new object[] { this.TagName });
case TagRenderMode.SelfClosing:
return string.Format(CultureInfo.InvariantCulture, "<{0}{1} />", new object[] { this.TagName, this.GetMyAttributesString() });
}
return string.Format(CultureInfo.InvariantCulture, "<{0}{1}>{2}</{0}>", new object[] { this.TagName, this.GetMyAttributesString(), this.InnerHtml });
}
//Implement GetMyAttributesString where the Attributes are changed to a SortedDictionary
private string GetMyAttributesString()
{
var builder = new StringBuilder();
var myDictionary = new SortedDictionary<string, string>(); //new
foreach (KeyValuePair<string, string> pair in this.Attributes) //new
{ //new
myDictionary.Add(pair.Key, pair.Value); //new
} //new
//foreach (KeyValuePair<string, string> pair in this.Attributes)
foreach (KeyValuePair<string, string> pair in myDictionary) //changed
{
string key = pair.Key;
if (!string.Equals(key, "id", StringComparison.Ordinal) || !string.IsNullOrEmpty(pair.Value))
{
string str2 = HttpUtility.HtmlAttributeEncode(pair.Value);
builder.AppendFormat(CultureInfo.InvariantCulture, " {0}=\"{1}\"", new object[] { key, str2 });
}
}
return builder.ToString();
}
}
I disassembled TagBuilder.ToString()
with Reflector, and this is the key bit of code:
foreach (KeyValuePair<string, string> pair in this.Attributes)
{
string key = pair.Key;
string str2 = HttpUtility.HtmlAttributeEncode(pair.Value);
builder.AppendFormat(CultureInfo.InvariantCulture, " {0}=\"{1}\"", new object[] { key, str2 });
}
So I would say not - this.Attributes
is an IDictionary<string,string>
interface, when enumerating over that "the order in which the items are returned is undefined," according to MSDN.
I did not want to override all that code to modify the sort behaviour so I changed the Attributes-property to a regular unsorted Dictionary with reflection instead
private class MyTagBuilder: TagBuilder
{
private static readonly MethodInfo tagBuilderAttrSetMethod = typeof(TagBuilder).GetProperty(nameof(Attributes)).SetMethod;
public MyTagBuilder(string tagName) : base(tagName)
{
// TagBuilder internally uses SortedDictionary, render attributes according to the order they are added instead
tagBuilderAttrSetMethod.Invoke(this, new object[] { new Dictionary<string, string>() });
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.