CSDN-CSDN社区-.NET技术-ASP.NET

收藏 以横向树方式显示Html表格 [问题点数:300,结帖人:jeremyyang824]

楼主发表于:2008-05-24 02:51:59
、结束(遍历到叶子节点)加上 5加上
最近项目中常要画动态的Table,由于HTML表格中纵向合并单元格使用的是rowspan属性,一旦遇到纵向合并单元格的时候就会特别显得特别麻烦。其实我们项目中所画的Table大多都是些树,如果以类似TreeView添加节点的方式来构建Table对象,最后调用重写的ToString方法把整个表格呈现出来应该效果不错,避免了在代码中充斥着大量的td、tr等字符串,影响了代码的可读性及易于维护性。于是我简单得作了个类库分享给大家,希望能够对大家有。
重写树节点INode的ToString方法使表格有不同的表现方式。


部分代码如下:
C# code

public interface INode : System.Collections.Generic.IEnumerable
{
    INode Parent { get;set;} //取得父结点
    INodeList Childs { get; } //取得下级节点
    INodeList Leafs { get;} //取得以该节点为根的子数的叶子节点
    bool IsLeaf { get;} //是否为叶子结点

    int Tier { get;} //取得该节点在树中的所处层数(从0开始计数)
    int Depth { get;} //取得以该节点为根的子数的深度(本层为0)

    IAttributeDictionary Attributes { get;set;} //该节点的属性集合

    object Content { get;set;} //节点中的内容

    #region children operation

    void AddChild(INode child);
    bool RemoveChild(INode child);
    void ClearChildren();

    #endregion

    string ToString(); //将节点和其属性以及内容表示为网页可显示的字符串
}

public interface ITree : INode
{
    //ITree FullFill(); //返回该树的"满数"
    ITree FullFill() where T : INode, new(); //泛型版本(用类型T来填充)

    new string ToString(); //1循环子节点 2调用INode的[前序遍历] 3调用INode.ToString(); 4每行开始
} /// /// Node 的摘要说明 /// public abstract class BaseNode : INode { protected INode parent; //父节点 protected IList childs = new List(); //子节点的"内部表现" public BaseNode() { } public BaseNode(INode parent) { parent.AddChild(this); this.parent = parent; } //private void initial() //{ //} #region INode 成员 /// /// 取得父结点 /// public INode Parent { get { return this.parent; } set { value.AddChild(this); //设定父节点的同时,在父节点的子列表中加入该结点 this.parent = value; } } /// /// 取得下级节点--子结点的外部表现(只读) /// public INodeList Childs { get { return new BaseNodeList(childs); } } /// /// 取得以该节点为根的子数的叶子节点 /// public INodeList Leafs { get { IList leafs = new List(); foreach (INode node in this) { //判断是否为叶子结点 if (node.Childs.Count == 0) leafs.Add(node); } return new BaseNodeList(leafs); } } /// /// 是否是叶子结点 /// public bool IsLeaf { get { if (this.childs.Count == 0) return true; return false; } } /// /// 取得该节点在树中的所处层数(从0开始计数) /// public int Tier { get { return this.getTier(this); } } /// /// 取得以该节点为根的子数的深度(本层为0) /// public int Depth { get { return this.getDepth(this); } } #region 抽象方法 /// /// 属性列表(根据其ToString方法来显示) /// public abstract IAttributeDictionary Attributes { get;set;} /// /// 结点内容(可以是任何对象,最终根据INode.ToString方法来显示) /// public abstract object Content { get;set;} /// /// 必须重写ToString方法,用于呈现该INode /// /// public abstract override string ToString(); #endregion #region 操作子结点 /// /// 添加一个子结点 /// /// public void AddChild(INode item) { INode oldParent = item.Parent; //原父结点 if (oldParent == null || oldParent.RemoveChild(item)) //从原父节点的子结点中移除该结点 { childs.Add(item); //item.Parent = this; //该句会导致无限递归错误! ((BaseNode)item).parent = this; } } /// /// 移除一个子结点 /// /// /// public bool RemoveChild(INode item) { if (this.childs.Remove(item)) { item.Parent = null; return true; } return false; } /// /// 清空子结点 /// public void ClearChildren() { foreach (INode node in childs) { childs.Remove(node); } } #endregion #endregion #region IEnumerable 成员 /// /// 先序遍历 /// /// public IEnumerator GetEnumerator() { Queue queueList = new Queue(); //按序进出的队列 perOrderTraverse(queueList, this); //本身不入队列 while (queueList.Count > 0 && queueList.Peek() != null) { yield return queueList.Dequeue(); } } #endregion #region IEnumerable 成员 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion /// /// 先序遍历,并按序入队列 /// private void perOrderTraverse(Queue queueList, INode parentNode) { foreach (INode node in parentNode.Childs) { queueList.Enqueue(node); perOrderTraverse(queueList, node); } } /// /// 取得深度 /// private int getDepth(INode node) { if (node == null || node.Childs.Count == 0) return 0; int[] childDepthArray = new int[node.Childs.Count]; INodeList childList = node.Childs; for (int i = 0; i < childList.Count; i++) { childDepthArray[i] = getDepth(childList[i]); } Array.Sort(childDepthArray); //升序排序 return childDepthArray[childDepthArray.Length - 1] + 1; //取得最大层数子树的层数 + 1 } /// /// 取得层数 /// private int getTier(INode node) { int tier = 0; INode n = node; while (n.Parent != null) { n = n.Parent; tier++; } return tier; } }
回复次数:14
#1楼 得分:0回复于:2008-05-24 02:54:12
C# code
/// <summary> /// Tree 的摘要说明 /// </summary> public class BaseTree : BaseNode, ITree { protected IAttributeDictionary attributes = new BaseAttributeDictionary(); //属性集合 #region ITree 成员 ///// <summary> ///// 返回该树的"满数" ///// </summary> ///// <returns></returns> //public virtual ITree FullFill() //{ // int treeHeight = this.Depth; //该树的高度 // INodeList leafs = this.Leafs; //该树的叶子集合 // foreach (INode node in leafs) // { // int tier = node.Tier; //某叶子结点的所在层数 // if (tier < treeHeight)//填到树的高度 // { // int length = treeHeight - tier; //需要填充的高度 // this.addFixLengthNode(length, node); // } // } // return this; //} /// <summary> /// 返回该树的"满数"(用T类型的结点填充) /// </summary> /// <returns></returns> public virtual ITree FullFill<T>() where T : INode, new() { int treeHeight = this.Depth; //该树的高度 INodeList leafs = this.Leafs; //该树的叶子集合 foreach (INode node in leafs) { int tier = node.Tier; //某叶子结点的所在层数 if (tier < treeHeight)//填到树的高度 { int length = treeHeight - tier; //需要填充的高度 this.addFixLengthNode<T>(length, node); } } return this; } #endregion #region IEnumerable 成员 public new IEnumerator GetEnumerator() { return base.GetEnumerator(); } #endregion public override IAttributeDictionary Attributes { get { return this.attributes; } set { this.attributes = value; } } public override object Content { get { return this.ToString(); } set { new Exception("不可更改内容"); } } /// <summary> /// 1循环子节点 /// 2调用INode的[前序遍历] /// 3调用INode.ToString(); /// 4每行开始<tr>、结束(遍历到叶子节点)加上<![CDATA[</tr> ]]> /// 5加上<![CDATA[<table></table>]]> /// </summary> /// <returns></returns> public override string ToString() { System.Text.StringBuilder builder = new System.Text.StringBuilder(); builder.Append("<table ").Append(this.attributes == null ? "" : this.attributes.ToString()).Append(">"); //加上table的属性 //多根循环 //foreach (INode rootNode in this.Childs) { builder.Append(@"<tr>"); foreach (INode node in this) //前序遍历 { //加上rowspan属性 countRowSpan(node); builder.Append(node.ToString()); if (node.IsLeaf) { builder.Append(@"</tr><tr>"); } } builder.Remove(builder.Length - 4, 4); //移除最后的<tr> } builder.Append(@"</table>"); return builder.ToString(); } /// <summary> /// 计算td的rowspan,并加上rowspan属性 /// </summary> /// <param name="node">原结点</param> protected virtual void countRowSpan(INode node) { int value = node.Leafs.Count; //计算叶子结点(rowspan的值) if (node.Attributes == null) { node.Attributes = new BaseAttributeDictionary(); } IAttri attri = new SingletonAttri("rowspan", value.ToString()); node.Attributes.Add(attri); //加上rowspan属性 } ///// <summary> ///// 填充固定长度线性树 ///// </summary> //private void addFixLengthNode(int length, INode parent) //{ // if (length < 1) // throw new Exception("无效长度,必须大于1"); // INode node = new SingletonNode(parent, "&nbsp;"); //空节点 // for (int i = 0; i < length - 1; i++) // { // INode tempNode = new SingletonNode("&nbsp;"); //空节点 // node.AddChild(tempNode); // node = tempNode; //持有下一个节点 // } //} /// <summary> /// 填充固定长度线性树(用类型T填充) /// </summary> private void addFixLengthNode<T>(int length, INode parent) where T : INode, new() { if (length < 1) throw new Exception("无效长度,必须大于1"); INode node = new T(); //空节点 node.Parent = parent; node.Content = "&nbsp;"; for (int i = 0; i < length - 1; i++) { INode tempNode = new T(); //空节点 tempNode.Content = "&nbsp"; node.AddChild(tempNode); node = tempNode; //持有下一个节点 } } }


INode具体实现 效果如图1
C# code
/// <summary> /// SingletonNode -- 单td树结点 /// </summary> public class SingletonNode : BaseNode { private string content; //td中的内容 private IAttributeDictionary attributes = new BaseAttributeDictionary(); //td中的属性集合 #region 构造器 public SingletonNode() { } public SingletonNode(string content) { this.content = content; } public SingletonNode(INode parent, string content) : base(parent) { this.content = content; } public SingletonNode(INode parent, string content, IAttributeDictionary attributes) : this(parent, content) { this.attributes = attributes; //属性列表 } #endregion public override IAttributeDictionary Attributes { get { return this.attributes; } set { this.attributes = value; } } public override object Content { get { return this.content; } set { this.content = value.ToString(); } } public override string ToString() { System.Text.StringBuilder builder = new System.Text.StringBuilder(); string attriStr = attributes.ToString(); //属性 string showContent = this.content; if (string.IsNullOrEmpty(content)) showContent = "&nbsp"; //若该td中内容为空则需要显示一个空格,否则该td会显示不出来 builder.Append(@"<td ").Append(attriStr).Append(@">").Append(showContent).Append(@"</td>"); return builder.ToString(); } }


#2楼 得分:0回复于:2008-05-24 02:58:26
INode的带索引实现 效果如图2
C# code
/// <summary> /// ContainIndexNode 包含index的结点 /// </summary> public class ContainIndexNode : BaseNode { private string content; //td2中的内容 private IAttributeDictionary attributes = new BaseAttributeDictionary(); //td中的属性集合 #region 构造器 public ContainIndexNode() { } public ContainIndexNode(string content) { this.content = content; } public ContainIndexNode(INode parent, string content) : base(parent) { this.content = content; } public ContainIndexNode(INode parent, string content, IAttributeDictionary attributes) : this(parent, content) { this.attributes = attributes; //属性列表 } #endregion public override IAttributeDictionary Attributes { get { return this.attributes; } set { this.attributes = value; } } public override object Content { get { return this.content; } set { this.content = value.ToString(); } } public override string ToString() { System.Text.StringBuilder builder = new System.Text.StringBuilder(); string attriStr = attributes.ToString(); //属性 string showContent = this.content; if (string.IsNullOrEmpty(content)) showContent = "&nbsp"; //若该td中内容为空则需要显示一个空格,否则该td会显示不出来 builder.Append(@"<td ").Append(attriStr).Append(@">").Append(this.getIndex()).Append(@"</td>"); builder.Append(@"<td ").Append(attriStr).Append(@">").Append(showContent).Append(@"</td>"); return builder.ToString(); } #region 取得索引 private string getIndex() { System.Text.StringBuilder builder = new System.Text.StringBuilder(); System.Collections.Generic.Stack<int> stack = new System.Collections.Generic.Stack<int>(); int parts = this.Tier; int index = 0; INode node = this; while (index < parts) { int num = this.getIndex(node); stack.Push(num); node = node.Parent; index++; } while (stack.Count > 0) { builder.Append(stack.Pop()).Append("."); } builder.Remove(builder.Length - 1, 1); //移除最后的点 return builder.ToString(); } private int getIndex(INode node) { INodeList list = node.Parent.Childs; for (int i = 0; i < list.Count; i++) { if (list[i] == node) return i + 1; } return 0; } #endregion }


具体的内容说明以及源代码下载可以到这里查看http://www.cnblogs.com/jeremyyang824/archive/2008/05/24/1206232.html
#4楼 得分:50回复于:2008-05-24 08:45:12
  • zengxie用户头像
  • zengxie
  • (我女儿和我们家的狗)
  • 等 级:
#5楼 得分:50回复于:2008-05-24 18:24:39
#6楼 得分:50回复于:2008-05-24 18:50:29
#7楼 得分:0回复于:2008-05-24 19:25:32
主要是在于动态的画表,如果数据内容是不断变化的要合并纵向单元格就不能用dw了吧。
一但一个td的rowspan有变,那么会影响到多个tr中的td的数量。
如果我是一个制定计划大纲的客户,从1条主任务,到下面的分支任务,再到再下层的实施步骤,一层一层细分下来填写的话,就只能动态的画出表格来,如果是用代码画过html表格的人一定会有体会。
  • wfyfngu用户头像
  • wfyfngu
  • (用C#连鸡蛋)
  • 等 级:
#8楼 得分:50回复于:2008-05-24 21:41:34
#9楼 得分:50回复于:2008-05-24 22:06:39
#10楼 得分:0回复于:2008-05-24 22:29:21
@wfyfngu
最终生成的是html表格的字符串,可以用ajax传到前台显示。
我js水平不太过关,纯js写比较累,呵呵 ...
#11楼 得分:50回复于:2008-05-25 00:01:50
#12楼 得分:0回复于:2008-05-25 11:18:25
引用 11 楼 elf512 的回复:
呵呵
用Repater 和DataList空间想怎么画就怎么画
那用这么麻烦呢

但是要纵向合并单元格就不方便了吧
重点在于对rowspan的控制
#13楼 得分:0回复于:2008-05-28 21:36:56
up~~~
#14楼 得分:0回复于:2008-06-09 17:36:17
谢谢所有看过的朋友~~
相关问题
以横向树方式显示Html表格,类似这种的JAVA代码如何写
怎样才能在网页中横向显示表中的一列数据?望高人指点!! .NET技术 ...
请问如何实现分层显示产品目录的效果?类似eachnet首页,跪求~ .NET ...
我是一位菜鸟~!我对HTML的代码不太了解~!想知到…………? Web 开发 ...