3

什么 C# 模板引擎
使用只有文本和标记的“纯”HTML
没有任何控制流,如ifwhile循环或表达式,
将 html 与控制代码分开?

下面是示例电话簿列表代码,
说明了应该如何完成:

string html=@" 
<html><head><title>@title</title></head>
<body>
<table>
<tr>
    <td> id</td> <td> name</td> <td> sex</td> <td>phones</td>
</tr><!--@contacts:-->
<tr>
    <td>@id</td> <td>@name</td> <td>@sex</td> 
      <td>
          <!--@phones:-->@phone <br/>
          <!--:@phones-->
      </td>
</tr><!--:@contacts-->
</table>
</body>
</html>";

var contacts = from c in db.contacts select c;

Marker m = new Marker(html);
Filler t = m.Mark("title");
t.Set("Phone book");

Filler c = m.Mark("contacts", "id,name,sex");

// **foreach** expressed in code, not in html 

foreach(var contact in contacts) {
    int id = contact.id;

    c.Add(id, contact.name, contact.sex);
    Filler p = c.Mark("phones", "phone");

    var phones = from ph in db.phones
                 where ph.id == id 
                 select new {ph.phone};

    if (phones.Any()) {
        foreach(var ph in phones) {
            p.Add(ph);
        }
    } else {
        fp.Clear();
    }
}
Console.Out.WriteLine(m.Get());
4

4 回答 4

2

只需使用 ASP.NET。无论您使用 Web 表单还是 MVC,在 .cs 文件中使用 C# 并在 .aspx 文件中使用 HTML 都非常容易。

与编程中的任何事情一样,99% 的事情都由你自己来做。灵活的 UI 引擎不会强制您遵循良好的编码实践。

于 2012-07-31T17:54:51.350 回答
2

使用此代码:

模板文件

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace templaten.com.Templaten
{
    public class tRange
    {
        public int head, toe;

        public tRange(int _head, int _toe)
        {
            head = _head;
            toe = _toe;
        }
    }

    public enum AType
    {
        VALUE = 0,
        NAME = 1,
        OPEN = 2,
        CLOSE = 3,
        GROUP = 4
    }

    public class Atom
    {
        private AType kin;
        private string tag;
        private object data;
        private List<Atom> bag;

        public Atom(string _tag = "",
                    AType _kin = AType.VALUE,
                    object _data = null)
        {
            tag = _tag;
            if (String.IsNullOrEmpty(_tag))
                _kin = AType.GROUP;
            kin = _kin;

            if (_kin == AType.GROUP)
                bag = new List<Atom>();
            else
                bag = null;

            data = _data;
        }

        public AType Kin
        {
            get { return kin; }
        }

        public string Tag
        {
            get { return tag; }
            set { tag = value; }
        }

        public List<Atom> Bag
        {
            get { return bag; }
        }

        public object Data
        {
            get { return data; }
            set { data = value; }
        }

        public int Add(string _tag = "",
                       AType _kin = AType.VALUE,
                       object _data = null)
        {
            if (bag != null)
            {
                bag.Add(new Atom(_tag, _kin, _data));
                return bag.Count - 1;
            }
            else
            {
                return -1;
            }
        }
    }

    public class Templet
    {
        private string content;

        string namepat = "\\w+";
        string justName = "(\\w+)";

        string namePre = "@";
        string namePost = "";

        string comment0 = "\\<!--\\s*";
        string comment1 = "\\s*--\\>";

        private Atom tokens;                         // parsed contents
        private Dictionary<string, int> iNames;      // name index
        private Dictionary<string, tRange> iGroups;  // groups index

        private Atom buffer;                         // output buffer
        private Dictionary<string, int> _iname;      // output name index
        private Dictionary<string, tRange> _igroup;  // output index

        public Templet(string Content = null)
        {
            Init(Content);
        }

        private int[] mark(string[] names, string group)
        {
            if (names == null || names.Length < 1) return null;
            tRange t = new tRange(0, buffer.Bag.Count - 1);
            if (group != null)
            {
                if (!_igroup.ContainsKey(group)) return null;
                t = _igroup[group];
            }

            int[] marks = new int[names.Length];

            for (int i = 0; i < marks.Length; i++)
                marks[i] = -1;

            for (int i = t.head; i <= t.toe; i++)
            {
                if (buffer.Bag[i].Kin == AType.NAME)
                {
                    for (int j = 0; j < names.Length; j++)
                    {
                        if (String.Compare(
                            names[j],
                            buffer.Bag[i].Tag,
                            true) == 0)
                        {
                            marks[j] = i;
                            break;
                        }
                    }
                }
            }
            return marks;
        }

        public Filler Mark(string group, string names)
        {
            Filler f = new Filler(this, names);
            f.di = mark(f.names, group);
            f.Group = group;
            tRange t = null;
            if (_igroup.ContainsKey(group)) t = _igroup[group];
            f.Range = t;
            return f;
        }

        public Filler Mark(string names)
        {
            Filler f = new Filler(this, names);

            f.di = mark(f.names, null);
            f.Group = "";
            f.Range = null;
            return f;
        }

        public void Set(int[] locations, object[] x)
        {
            int j = Math.Min(x.Length, locations.Length);
            for (int i = 0; i < j; i++)
            {
                int l = locations[i];

                if ((l >= 0) && (buffer.Bag[l] != null))
                    buffer.Bag[l].Data = x[i];
            }
        }

        public void New(string group, int seq = 0)
        {
            // place new group copied from old group just below it

            if (!( iGroups.ContainsKey(group)
                && _igroup.ContainsKey(group)
                && seq > 0)) return;

            tRange newT = null;
            tRange t = iGroups[group];
            int beginRange = _igroup[group].toe + 1;

            for (int i = t.head; i <= t.toe; i++)
            {
                buffer.Bag.Insert(beginRange,
                    new Atom(tokens.Bag[i].Tag,
                        tokens.Bag[i].Kin,
                        tokens.Bag[i].Data));
                beginRange++;
            }

            newT = new tRange(t.toe + 1, t.toe + (t.toe - t.head + 1));

            // rename past group
            string pastGroup = group + "_" + seq;
            t = _igroup[group];
            buffer.Bag[t.head].Tag = pastGroup;
            buffer.Bag[t.toe].Tag = pastGroup;

            _igroup[pastGroup] = t;

            // change group indexes
            _igroup[group] = newT;


        }

        public void ReMark(Filler f, string group)
        {
            if (!_igroup.ContainsKey(group)) return;
            Map(buffer, _iname, _igroup);
            f.di = mark(f.names, group);
            f.Range = _igroup[group];
        }

        private static void Indexing(string aname,
            AType kin,
            int i,
            Dictionary<string, int> dd,
            Dictionary<string, tRange> gg)
        {
            switch (kin)
            {
                case AType.NAME: // index all names
                    dd[aname] = i;
                    break;
                case AType.OPEN: // index all groups
                    if (!gg.ContainsKey(aname))
                        gg[aname] = new tRange(i, -1);
                    else
                        gg[aname].head = i;
                    break;
                case AType.CLOSE:
                    if (!gg.ContainsKey(aname))
                        gg[aname] = new tRange(-1, i);
                    else
                        gg[aname].toe = i;
                    break;
                default:
                    break;
            }
        }

        private static void Map(Atom oo,
            Dictionary<string, int> dd,
            Dictionary<string, tRange> gg)
        {

            for (int i = 0; i < oo.Bag.Count; i++)
            {
                string aname = oo.Bag[i].Tag;
                Indexing(oo.Bag[i].Tag, oo.Bag[i].Kin, i, dd, gg);
            }
        }

        public void Init(string Content = null)
        {
            content = Content;

            tokens = new Atom("", AType.GROUP);

            iNames = new Dictionary<string, int>();
            iGroups = new Dictionary<string, tRange>();

            // parse content into tokens
            string namePattern = namePre + namepat + namePost;
            string patterns =
                "(?<var>" + namePattern + ")|" +
                "(?<head>" + comment0 + namePattern + ":" + comment1 + ")|" +
                "(?<toe>" + comment0 + ":" + namePattern + comment1 + ")";
            Regex jn = new Regex(justName, RegexOptions.Compiled);
            Regex r = new Regex(patterns, RegexOptions.Compiled);
            MatchCollection ms = r.Matches(content);
            int pre = 0;
            foreach (Match m in ms)
            {
                tokens.Add(content.Substring(pre, m.Index - pre));
                int idx = -1;
                if (m.Groups.Count >= 3)
                {
                    string aname = "";
                    MatchCollection x = jn.Matches(m.Value);
                    if (x.Count > 0 && x[0].Groups.Count > 1)
                        aname = x[0].Groups[1].ToString();
                    AType t = AType.VALUE;

                    if (m.Groups[1].Length > 0) t = AType.NAME;
                    if (m.Groups[2].Length > 0) t = AType.OPEN;
                    if (m.Groups[3].Length > 0) t = AType.CLOSE;
                    if (aname.Length > 0)
                    {
                        tokens.Add(aname, t);

                        idx = tokens.Bag.Count - 1;
                    }
                    Indexing(aname, t, idx, iNames, iGroups);
                }
                pre = m.Index + m.Length;
            }
            if (pre < content.Length)
                tokens.Add(content.Substring(pre, content.Length - pre));

            // copy tokens into buffer
            buffer = new Atom("", AType.GROUP);
            for (int i = 0; i < tokens.Bag.Count; i++)
                buffer.Add(tokens.Bag[i].Tag, tokens.Bag[i].Kin);

            // initialize index of output names
            _iname = new Dictionary<string, int>();
            foreach (string k in iNames.Keys)
                _iname[k] = iNames[k];

            // initialize index of output groups
            _igroup = new Dictionary<string, tRange>();
            foreach (string k in iGroups.Keys)
            {
                tRange t = iGroups[k];
                _igroup[k] = new tRange(t.head, t.toe);
            }
        }

        public string Get()
        {
            StringBuilder sb = new StringBuilder("");
            for (int i = 0; i < buffer.Bag.Count; i++)
            {
                switch (buffer.Bag[i].Kin)
                {
                    case AType.VALUE:
                        sb.Append(buffer.Bag[i].Tag);
                        break;
                    case AType.NAME:
                        sb.Append(buffer.Bag[i].Data);
                        break;
                    case AType.OPEN:
                    case AType.CLOSE:
                        break;
                    default: break;
                }
            }
            return sb.ToString();
        }


    }

    public class Filler
    {
        private Templet t = null;

        public int[] di;
        public string[] names;
        public string Group { get; set; }
        public tRange Range { get; set; }
        private int seq = 0;

        public Filler(Templet tl, string markers = null)
        {
            t = tl;
            if (markers != null)
                names = markers.Split(new char[] { ',' },
                    StringSplitOptions.RemoveEmptyEntries);
            else
                names = null;
        }

        public void init(int length)
        {
            di = new int[length];
            for (int i = 0; i < length; i++)
                di[i] = -1;
            seq = 0;
            Group = "";
            Range = null;
        }

        // clear contents inside marked object or group
        public void Clear()
        {
            object[] x = new object[di.Length];
            for (int i = 0; i < di.Length; i++)
                x[i] = null;
            t.Set(di, x);
        }

        // set value for marked object,
        // or add row to group and set value to columns
        public void Set(params object[] x)
        {
            t.Set(di, x);
        }

        public void Add(params object[] x)
        {
            if (Group.Length > 0)
            {
                t.New(Group, seq);
                ++seq;
                t.ReMark(this, Group);
            }

            t.Set(di, x);
        }
    }
}

测试程序

程序.cs

Templet m = new Templet(html);
Filler f= m.Mark("title");
f.Set("Phone book");
Filler fcontacts = m.Mark("contacts", "id,name,sex,phone");
fcontacts.Add(1, "Akhmad", "M", "123456");
fcontacts.Add(2, "Barry", "M", "234567");
fcontacts.Add(1, "Charles", "M", "345678");
Console.Out.WriteLine(m.Get());

仍然不能做嵌套循环。

于 2012-08-01T18:20:00.410 回答
1

原则上,您选择的大多数模板引擎都可以使用适当的架构将 HTML 与控制逻辑分开。使用 MVC(或 MVVM)模式,如果您以控制器包含 if/then 逻辑而不是视图的方式构建模型,则可以将其从视图中消除。

也就是说,您使用的语法非常接近Razor语法,后者可通过 NuGet 包轻松用于 ASP.NET MVC。

于 2012-07-31T17:45:54.403 回答
0

我完全听到你的声音。我构建了 SharpFusion,其中包含一些其他内容,但是如果您查找 template.cs 文件,您将看到解析 HTML 文件并简单地将标记替换为您在 c# 中创建的值的处理程序。

因为没有像 ASP.NET 那样完成 XML 解析,所以该框架的加载速度甚至比 MVC 站点还要快。另一种选择是 ServiceStack。

于 2012-10-23T19:18:57.667 回答