Pouvez-vous contrôler l'ordre dans lequel les attributs rendent classe TagBuilder?
-
27-09-2019 - |
Question
Je sais que ce genre est obsessionnel, mais est-il un moyen de contrôler l'ordre que la classe TagBuilder
rend les attributs d'une balise HTML lorsque vous appelez ToString()
?
i.e.. de sorte que
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)
renverra
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
pas
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
Modification de l'ordre que vous ajoutez les attributs ne change pas, il semble être rendu dans l'ordre alphabétique
La solution
Essayez d'utiliser cette classe, qui hérite de la TagBuilder et remplace la méthode ToString, la construction d'un SortedDictionary des attributs et en utilisant ce dictionnaire pour rendre.
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();
}
}
Autres conseils
Je démonte TagBuilder.ToString()
avec réflecteur, ce qui est le bit clé de 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 });
}
Je dirais donc pas - this.Attributes
est une interface IDictionary<string,string>
, quand sur cette énumération « l'ordre dans lequel les articles sont retournés n'est pas défini », selon MSDN.
Je ne voulais pas passer outre tout ce code pour modifier le comportement de tri alors j'ai changé les attributs-propriété à un dictionnaire régulier avec la réflexion au lieu Unsorted
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>() });
}
}