【问题标题】:Can you control the order in which the TagBuilder class renders attributes?你能控制 TagBuilder 类呈现属性的顺序吗?
【发布时间】:2011-03-28 18:50:54
【问题描述】:

我知道这有点强迫症,但是当您调用ToString() 时,有没有办法控制TagBuilder 类呈现HTML 标记的属性的顺序?

即这样

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" />

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

【问题讨论】:

    标签: c# asp.net tagbuilder


    【解决方案1】:

    尝试使用这个类,它继承 TagBuilder 并覆盖 ToString 方法,从 Attributes 构建一个 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】:

      我用 Reflector 反汇编了TagBuilder.ToString(),这是代码的关键位:

      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&lt;string,string&gt; 接口,当枚举“项目返回的顺序未定义”时,根据 MSDN。

      【讨论】:

      • 仅供参考,如果您查看标签构建器构造函数,IDictionary 实际上是一个 SortedDictionary
      【解决方案3】:

      我不想重写所有代码来修改排序行为,所以我将 Attributes-property 更改为带有反射的常规未排序字典

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

      【讨论】:

        猜你喜欢
        • 2011-06-27
        • 1970-01-01
        • 1970-01-01
        • 2022-11-10
        • 2011-04-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-16
        相关资源
        最近更新 更多