用于呈现HTML的流畅界面

Pat*_*gne 10 .net html render web-controls htmltextwriter

在我看来,使用HtmlTextWriter渲染HTML并不是非常直观,但如果您在Web表单中实现Web控件,那么您必须使用它.我认为有可能为此创建一个流畅的界面,它更像是它输出的HTML.我想知道人们对我到目前为止提出的语法的看法.

    public void Render(HtmlTextWriter writer)
    {
        writer
            .Tag(HtmlTextWriterTag.Div, e => e[HtmlTextWriterAttribute.Id, "id"][HtmlTextWriterAttribute.Name,"name"][HtmlTextWriterAttribute.Class,"class"])
                .Tag(HtmlTextWriterTag.Span)
                    .Text("Lorem")
                .EndTag()
                .Tag(HtmlTextWriterTag.Span)
                    .Text("ipsum")
                .EndTag()
            .EndTag();        
    }
Run Code Online (Sandbox Code Playgroud)

"Tag","Text"和"EndTag"是HtmlTextWriter类的扩展方法,它返回它所接受的实例,以便可以链接调用.传递给第一次调用"Tag"时使用的重载中使用的lambda的参数是一个"HtmlAttributeManager",它是一个简单的类,它包装一个HtmlTextWriter来提供一个索引器,它接受一个HtmlTextWriterAttribute和一个字符串值并返回实例所以这些电话可以被链接.我也有这个类的方法用于最常见的属性,例如"Name","Class"和"Id",这样你就可以编写上面的第一个调用,如下所示:

.Tag(HtmlTextWriterTag.Div, e => e.Id("id").Name("name").Class("class"))
Run Code Online (Sandbox Code Playgroud)

一个更长的例子:

public void Render(HtmlTextWriter writer)
{
    writer
        .Tag(HtmlTextWriterTag.Div, a => a.Class("someClass", "someOtherClass"))
            .Tag(HtmlTextWriterTag.H1).Text("Lorem").EndTag()
            .Tag(HtmlTextWriterTag.Select, t => t.Id("fooSelect").Name("fooSelect").Class("selectClass"))
                .Tag(HtmlTextWriterTag.Option, t => t[HtmlTextWriterAttribute.Value, "1"][HtmlTextWriterAttribute.Title, "Selects the number 1."])
                    .Text("1")
                .EndTag(HtmlTextWriterTag.Option)
                .Tag(HtmlTextWriterTag.Option, t => t[HtmlTextWriterAttribute.Value, "2"][HtmlTextWriterAttribute.Title, "Selects the number 2."])
                    .Text("2")
                .EndTag(HtmlTextWriterTag.Option)
                .Tag(HtmlTextWriterTag.Option, t => t[HtmlTextWriterAttribute.Value, "3"][HtmlTextWriterAttribute.Title, "Selects the number 3."])
                    .Text("3")
                .EndTag(HtmlTextWriterTag.Option)
            .EndTag(HtmlTextWriterTag.Select)
        .EndTag(HtmlTextWriterTag.Div);
}
Run Code Online (Sandbox Code Playgroud)

希望你能够"破译"这个片段输出的HTML,至少这个想法.

请给我任何关于如何改进语法的想法,可能是更好的方法名称,也许是其他一些方法.

编辑:我认为,如果不使用流畅的界面,看看相同的代码片段会是什么样子可能会很有趣,以便进行比较:

public void RenderUsingHtmlTextWriterStandardMethods(HtmlTextWriter writer)
{
    writer.AddAttribute(HtmlTextWriterAttribute.Class, "someClass someOtherClass");
    writer.RenderBeginTag(HtmlTextWriterTag.Div);

    writer.RenderBeginTag(HtmlTextWriterTag.H1);
    writer.Write("Lorem");
    writer.RenderEndTag();

    writer.AddAttribute(HtmlTextWriterAttribute.Id, "fooSelect");
    writer.AddAttribute(HtmlTextWriterAttribute.Name, "fooSelect");
    writer.AddAttribute(HtmlTextWriterAttribute.Class, "selectClass");
    writer.RenderBeginTag(HtmlTextWriterTag.Select);

    writer.AddAttribute(HtmlTextWriterAttribute.Value, "1");
    writer.AddAttribute(HtmlTextWriterAttribute.Title, "Selects the number 1.");
    writer.RenderBeginTag(HtmlTextWriterTag.Option);
    writer.Write("1");
    writer.RenderEndTag();

    writer.AddAttribute(HtmlTextWriterAttribute.Value, "2");
    writer.AddAttribute(HtmlTextWriterAttribute.Title, "Selects the number 2.");
    writer.RenderBeginTag(HtmlTextWriterTag.Option);
    writer.Write("2");
    writer.RenderEndTag();

    writer.AddAttribute(HtmlTextWriterAttribute.Value, "3");
    writer.AddAttribute(HtmlTextWriterAttribute.Title, "Selects the number 3.");
    writer.RenderBeginTag(HtmlTextWriterTag.Option);
    writer.Write("3");
    writer.RenderEndTag();

    writer.RenderEndTag();

    writer.RenderEndTag();
}
Run Code Online (Sandbox Code Playgroud)

编辑:我应该更明确一点,因为其中一个目标是它应该产生尽可能少的开销,这就是为什么我限制了lambdas的使用.另外,我首先使用了一个表示标记的类,以便在渲染之前通过语法构建类似于DOM树的东西,但语法非常相似.我放弃了这个解决方案,因为它会产生轻微的内存开销.在使用HtmlAttributeManager类时仍然存在一些这样的问题,我一直在考虑使用扩展方法来追加属性,但是我不能使用索引器语法,它也会膨胀HtmlTextWriter的接口更.

Kon*_*lph 3

我看到有两个问题:

\n\n
    \n
  • 重复使用Tag(Tagname, \xe2\x80\xa6)。为什么不为每个标签名称提供扩展方法?不可否认,这使界面变得臃肿,并且需要编写大量代码(=>代码生成!)。
  • \n
  • 编译器/IDE 无法帮助您。特别是,它不检查缩进(当您自动缩进时它甚至会破坏它)。
  • \n
\n\n

这两个问题也许可以通过使用 Lambda 方法来解决:

\n\n
writer.Write(body => new Tag[] {\n    new Tag(h1 => "Hello, world!"),\n    new Tag(p => "Indeed. What a lovely day.", new Attr[] {\n        new Attr("style", "color: red")\n    })\n});\n
Run Code Online (Sandbox Code Playgroud)\n\n

这只是一种基本方法。API 肯定需要做更多的工作。特别是,由于参数名称冲突,嵌套相同的标记名称将不起作用。另外,这个接口不能很好地(或者根本不能)与 VB 一起工作。但不幸的是,同样的情况也适用于其他现代 .NET API,甚至是 Microsoft 的 PLINQ 接口。

\n\n

我前一段时间考虑过的另一种方法实际上是尝试模仿 Markaby,例如 sambo 的代码。主要区别在于我使用的是using块而不是foreach,从而利用了 RAII:

\n\n
using (var body = writer.body("xml:lang", "en")) {\n    using (var h1 = body.h1())\n        h1.AddText("Hello, World!");\n    using (var p = body.p("style", "color: red"))\n        p.AddText("Indeed. What a lovely day.");\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

此代码不存在其他方法的问题。另一方面,它为属性提供的类型安全性较差,接口也不太优雅(对于给定的 \xe2\x80\x9c优雅的\xe2\x80\x9d 定义)。

\n\n

我编译了这两个代码,甚至生成了一些或多或少有意义的输出(即:HTML!)。

\n