代码之家  ›  专栏  ›  技术社区  ›  Glenn Slaven

你能控制TagBuilder类呈现属性的顺序吗?

  •  5
  • Glenn Slaven  · 技术社区  · 14 年前

    我知道这有点让人着迷,但是有没有一种方法可以控制 TagBuilder 类在调用时呈现HTML标记的属性 ToString() ?

    也就是说

    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)
    

    将返回

    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    

    <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
    

    更改添加属性的顺序不会改变它,它似乎是按字母顺序呈现的。

    3 回复  |  直到 6 年前
        1
  •  3
  •   Daniel Dyson    14 年前

    尝试使用这个类,它继承了TagBuilder并重写ToString方法,从属性构建一个SortedDictionary,并使用该字典进行呈现。

        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();
            }
        }
    
        2
  •  1
  •   Carson63000    14 年前

    我解体 TagBuilder.ToString() 使用Reflector,这是代码的关键部分:

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

    所以我会说不- this.Attributes 是一个 IDictionary<string,string> 根据msdn,当枚举“返回项的顺序未定义”时,接口。

        3
  •  0
  •   magnus e    6 年前

    我不想重写所有代码来修改排序行为,所以我改为使用反射将attributes属性更改为常规的未排序字典。

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