=IF(DATEDIF(A1, B1, "D")>365, QUOTIENT(DATEDIF(A1, B1, "D"), 365)&" year(s) "&
QUOTIENT(MOD(DATEDIF(A1, B1, "D"), 365), 30)&" month(s) "&
MOD(QUOTIENT(MOD(DATEDIF(A1, B1, "D"), 365), 30), 30)&" day(s)",
IF(DATEDIF(A1, B1, "D")>30, QUOTIENT(DATEDIF(A1, B1, "D"), 30)&" month(s) "&
MOD(DATEDIF(A1, B1, "D"), 30)&" day(s)",
DATEDIF(A1, B1, "D")&" day(s)"))
Я вижу две проблемы:
(Tagname,…)
. Почему бы не предложить методы расширения для каждого имени тега? По общему признанию, это раздувает интерфейс и довольно много пишет (=> генерация кода!). Обе проблемы могут быть решены с помощью лямбда-подхода:
writer.Write(body => new Tag[] {
new Tag(h1 => "Hello, world!"),
new Tag(p => "Indeed. What a lovely day.", new Attr[] {
new Attr("style", "color: red")
})
});
Это всего лишь один базовый подход. API, безусловно, потребуется гораздо больше работы. В частности, вложение одного и того же тега не будет работать из-за конфликтов имен аргументов. Кроме того, этот интерфейс не будет работать (или вообще) с VB. Но, к сожалению, то же самое относится и к другим современным API .NET, даже к интерфейсу PLINQ от Microsoft.
Другой подход, о котором я думал некоторое время назад, на самом деле пытается эмулировать Markaby, как код самбо. Основное отличие состоит в том, что я использую , используя блоки
вместо foreach
, таким образом, используя RAII:
using (var body = writer.body("xml:lang", "en")) {
using (var h1 = body.h1())
h1.AddText("Hello, World!");
using (var p = body.p("style", "color: red"))
p.AddText("Indeed. What a lovely day.");
}
Этот код не имеет проблем другого подхода. С другой стороны, он обеспечивает меньшую безопасность типов для атрибутов и менее элегантный интерфейс (для данного определения « элегантный »).
Я получаю оба кода для компиляции и даже создаю еще несколько или менее значимый вывод (т. е. HTML!).
у него нет проблем с другим подходом. С другой стороны, он обеспечивает меньшую безопасность типов для атрибутов и менее элегантный интерфейс (для данного определения « элегантный »).Я получаю оба кода для компиляции и даже создаю еще несколько или менее значимый вывод (т. е. HTML!).
у него нет проблем с другим подходом. С другой стороны, он обеспечивает меньшую безопасность типов для атрибутов и менее элегантный интерфейс (для данного определения « элегантный »).Я получаю оба кода для компиляции и даже создаю еще несколько или менее значимый вывод (т. е. HTML!).
Если вам нужно делать много такого рода вещей, рассматривали ли вы какой-нибудь шаблонизатор, такой как NHaml? 1287 В Ruby / Markaby это выглядело бы намного красивее.
div :class=>"someClass someOtherClass" do
h1 "Lorem"
select :id => "fooSelect", :name => "fooSelect", :class => "selectClass" do
option :title=>"selects the number 1", :value => 1 { "1" }
option :title=>"selects the number 2", :value => 2 { "2" }
option :title=>"selects the number 3", :value => 3 { "3" }
end
end
Вы можете портировать аналогичный подход на .Net
using(var d = HtmlTextWriter.Div.Class("hello"))
{
d.H1.InnerText("Lorem");
using(var s = d.Select.Id("fooSelect").Name("fooSelect").Class("fooClass"))
{
s.Option.Title("select the number 1").Value("1").InnerText("1");
}
}
Я думаю, что он читает вполне будет и поддерживает вложение.
РЕДАКТИРОВАТЬ Я украл использование у Конрада, потому что оно читается намного лучше.
У меня есть следующие проблемы с исходным предложением.
Мой предложенный подход потенциально немного менее эффективен, но я думаю, что он решает эти проблемы и будет очень прост в использовании.
Это то, что я придумал, позаботившись о следующих соображениях:
T.Tag
после , используя T = HtmlTextWriterTag ;
,
что может вам понравиться или нет Debug.Assert
приведен только для краткости, намерение должно быть ясным) Я не хотел оборачивать мириады методов HtmlTextWriter.
с помощью T = HtmlTextWriterTag;
public class HtmlBuilder {
Публичный делегат void Statement (HtmlTextWriter htmlTextWriter);
public HtmlBuilder (HtmlTextWriter htmlTextWriter) {
this.writer = htmlTextWriter;
}
// Начать оператор для тега; обязательный, 1-й оператор
public HtmlBuilder B (оператор утверждения) {
Debug.Assert (this.renderStatements.Count == 0);
this.renderStatements.Add (инструкция);
вернуть это;
}
// Операторы атрибутов для тега; необязательно, от 2-го до n-го операторов
public HtmlBuilder A (оператор утверждения) {
Debug.Assert (this.renderStatements.Count> 0);
this.renderStatements.Insert (this.cntBeforeStatements ++, инструкция);
вернуть это;
}
// Завершить оператор для тега; обязательное, последнее утверждение
// нет возвращаемого значения, на этом блок должен остановиться
public void E () {
Debug.Assert (this.renderStatements.Count> 0);
this.renderStatements.Add (i => {i.RenderEndTag ();});
foreach (оператор renderStatement в this.renderStatements) {
renderStatement (this.writer);
}
this.renderStatements.Clear (); this.cntBeforeStatements = 0;
}
частный int cntBeforeStatements = 0;
закрытый только для чтения List renderStatements = new List ();
частный писатель HtmlTextWriter только для чтения;
}
public class HtmlWriter {
публичный делегат void BlockWithHtmlTextWriter (HtmlTextWriter htmlTextWriter);
публичный делегат void BlockWithHtmlBuilder (HtmlBuilder htmlBuilder);
общедоступная строка Render (блок BlockWithHtmlTextWriter) {
StringBuilder stringBuilder = новый StringBuilder ();
using (StringWriter stringWriter = new StringWriter (stringBuilder)) {
using (HtmlTextWriter htmlTextWriter = new HtmlTextWriter (stringWriter)) {
блок (htmlTextWriter);
}
}
вернуть stringBuilder.ToString ();
}
общедоступная строка Render (BlockWithHtmlBuilder block) {
вернуть this.Render ((HtmlTextWriter htmlTextWriter) =>
блок (новый HtmlBuilder (htmlTextWriter)));
}
// небольшой тест / образец
static void Main (string [] args) {
HtmlWriter htmlWriter = новый HtmlWriter ();
System.Console.WriteLine (htmlWriter.Render ((HtmlBuilder b) => {
bB (h => h.RenderBeginTag (T.Div))
.A (h => h.AddAttribute ("foo", "bar"))
.A (h => h.AddAttribute ("doh", "baz"))
.E ();
}));
}
}
Я хотел чтобы иметь такой синтаксис:
using (var w = new HtmlTextWriter(sw))
{
w.Html()
.Head()
.Script()
.Attributes(new { type = "text/javascript", src = "somescript.cs" })
.WriteContent("var foo='bar'")
.EndTag()
.EndTag()
.Body()
.P()
.WriteContent("some content")
.EndTag()
.EndTag()
.EndTag();
}
Чтобы добиться этого, я добавил методы расширения в HtmlTextWriter, хотя контейнер, вероятно, был бы более подходящим (меня больше интересовало, чтобы он работал в первую очередь!) Чувствуя себя ленивым, я не хотел писать метод для каждого из доступных тегов, поэтому я кодирую методы, используя шаблон t4, повторяя перечисление System.Web.UI.HtmlTextWriterTag. Атрибуты тегов управляются с помощью анонимных объектов; код в основном отражает анонимный тип, извлекает свойства и превращает их в атрибуты, которые, как мне кажется, придают результирующему синтаксису очень чистый вид.
Результат кодовой легенды:
using System;
using System.Web.UI;
using System.Collections.Generic;
/// <summary>
/// Extensions for HtmlTextWriter
/// </summary>
public static partial class HtmlWriterTextTagExtensions
{
static Stack<Tag> tags = new Stack<Tag>();
/// <summary>
/// Opens a Unknown Html tag
/// </summary>
public static HtmlTextWriter Unknown(this HtmlTextWriter writer)
{
WritePreceeding(writer);
tags.Push(new Tag("Unknown", null));
return writer;
}
/// <summary>
/// Opens a A Html tag
/// </summary>
public static HtmlTextWriter A(this HtmlTextWriter writer)
{
WritePreceeding(writer);
tags.Push(new Tag("a", null));
return writer;
}
/// <summary>
/// Opens a Acronym Html tag
/// </summary>
public static HtmlTextWriter Acronym(this HtmlTextWriter writer)
{
WritePreceeding(writer);
tags.Push(new Tag("acronym", null));
return writer;
}
/// <summary>
/// Opens a Address Html tag
/// </summary>
public static HtmlTextWriter Address(this HtmlTextWriter writer)
{
WritePreceeding(writer);
tags.Push(new Tag("address", null));
return writer;
}
/// <summary>
/// Opens a Area Html tag
/// </summary>
public static HtmlTextWriter Area(this HtmlTextWriter writer)
{
WritePreceeding(writer);
tags.Push(new Tag("area", null));
return writer;
}
/// <summary>
/// Opens a B Html tag
/// </summary>
public static HtmlTextWriter B(this HtmlTextWriter writer)
{
WritePreceeding(writer);
tags.Push(new Tag("b", null));
return writer;
}
/// <summary>
/// Opens a Base Html tag
/// </summary>
public static HtmlTextWriter Base(this HtmlTextWriter writer)
{
WritePreceeding(writer);
tags.Push(new Tag("base", null));
return writer;
}
/// <summary>
/// Opens a Basefont Html tag
/// </summary>
public static HtmlTextWriter Basefont(this HtmlTextWriter writer)
{
WritePreceeding(writer);
tags.Push(new Tag("basefont", null));
return writer;
}
/// <summary>
/// Opens a Bdo Html tag
/// </summary>
public static HtmlTextWriter Bdo(this HtmlTextWriter writer)
{
WritePreceeding(writer);
tags.Push(new Tag("bdo", null));
return writer;
}
/// <summary>
/// Opens a Bgsound Html tag
/// </summary>
public static HtmlTextWriter Bgsound(this HtmlTextWriter writer)
{
WritePreceeding(writer);
tags.Push(new Tag("bgsound", null));
return writer;
}
/// <summary>
/// Opens a Big Html tag
/// </summary>
public static HtmlTextWriter Big(this HtmlTextWriter writer)
{
WritePreceeding(writer);
tags.Push(new Tag("big", null));
return writer;
}
/// <summary>
/// Opens a Blockquote Html tag
/// </summary>
public static HtmlTextWriter Blockquote(this HtmlTextWriter writer)
{
WritePreceeding(writer);
tags.Push(new Tag("blockquote", null));
return writer;
}
/// <summary>
/// Opens a Body Html tag
/// </summary>
public static HtmlTextWriter Body(this HtmlTextWriter writer)
{
WritePreceeding(writer);
tags.Push(new Tag("body", null));
return writer;
}
/// <summary>
/// Opens a Br Html tag
/// </summary>
public static HtmlTextWriter Br(this HtmlTextWriter writer)
{
WritePreceeding(writer);
tags.Push(new Tag("br", null));
return writer;
}
/// <summary>
/// Opens a Button Html tag
/// </summary>
public static HtmlTextWriter Button(this HtmlTextWriter writer)
{
WritePreceeding(writer);
tags.Push(new Tag("button", null));
return writer;
}
/// <summary>
/// Opens a Caption Html tag
/// </summary>
public static HtmlTextWriter Caption(this HtmlTextWriter writer)
{
WritePreceeding(writer);
tags.Push(new Tag("caption", null));
return writer;
}
/// <summary>
/// Opens a Center Html tag
/// </summary>
public static HtmlTextWriter Center(this HtmlTextWriter writer)
{
WritePreceeding(writer);
tags.Push(new Tag("center", null));
return writer;
}
/// <summary>
/// Opens a Cite Html tag
/// </summary>
public static HtmlTextWriter Cite(this HtmlTextWriter writer)
{
WritePreceeding(writer);
tags.Push(new Tag("cite", null));
return writer;
}
/// <summary>
/// Opens a Code Html tag
/// </summary>
public static HtmlTextWriter Code(this HtmlTextWriter writer)
{
WritePreceeding(writer);
tags.Push(new Tag("code", null));
return writer;
}
/// <summary>
/// Opens a Col Html tag
/// </summary>
public static HtmlTextWriter Col(this HtmlTextWriter writer)
{
WritePreceeding(writer);
tags.Push(new Tag("col", null));
return writer;
}
/// <summary>
/// Opens a Colgroup Html tag
/// </summary>
public static HtmlTextWriter Colgroup(this HtmlTextWriter writer)
{
WritePreceeding(writer);
tags.Push(new Tag("colgroup", null));
return writer;
}
/// <summary>
/// Opens a Dd Html tag
/// </summary>
public static HtmlTextWriter Dd(this HtmlTextWriter writer)
{
WritePreceeding(writer);
tags.Push(new Tag("dd", null));
return writer;
}
/// <summary>
/// Opens a Del Html tag
/// </summary>
public static HtmlTextWriter Del(this HtmlTextWriter writer)
{
WritePreceeding(writer);
tags.Push(new Tag("del", null));
return writer;
}
/// <summary>
/// Opens a Dfn Html tag
/// </summary>
public static HtmlTextWriter Dfn(this HtmlTextWriter writer)
{
WritePreceeding(writer);
tags.Push(new Tag("dfn", null));
return writer;
}
/// <summary>
/// Opens a Dir Html tag
/// </summary>
public static HtmlTextWriter Dir(this HtmlTextWriter writer)
{
WritePreceeding(writer);
tags.Push(new Tag("dir", null));
return writer;
}
/// <summary>
/// Opens a Div Html tag
/// </summary>
public static HtmlTextWriter Div(this HtmlTextWriter writer)
{
WritePreceeding(writer);
tags.Push(new Tag("div", null));
return writer;
}
/// <summary>
/// Opens a Dl Html tag
/// </summary>
public static HtmlTextWriter Dl(this HtmlTextWriter writer)
{
WritePreceeding(writer);
tags.Push(new Tag("dl", null));
return writer;
}
/// <summary>
/// Opens a Dt Html tag
/// </summary>
public static HtmlTextWriter Dt(this HtmlTextWriter writer)
{
WritePreceeding(writer);
tags.Push(new Tag("dt", null));
return writer;
}
/// <summary>
/// Opens a Em Html tag
/// </summary>
public static HtmlTextWriter Em(this HtmlTextWriter writer)
{
WritePreceeding(writer);
tags.Push(new Tag("em", null));
return writer;
}