ASP.NET中应用Excel:(6)在服务器端生成HTML表格

来源:互联网 发布:js正则判断数字和字母 编辑:程序博客网 时间:2024/05/18 23:55

读写数据都完成了,现在来看看如何生成客户端界面。使用Atals的TabContainer是个不错的选择。比较的是如何保持单元格原有的格式,特别是在有单元格合并的情况下。

首先,你得有一个TabContainer控件在页面中,然后需要根据工作表的数量添加相应的TabPanel。可以以如下方式添加:

XmlNodeList sheet_node_list = xml.GetElementsByTagName("WORKSHEET");for (int m = 0; m < sheet_node_list.Count; m++) // 遍历所有的工作表{    AjaxControlToolkit.TabPanel panel = null; // TabPanel控件,我们的表格将放在其中    if (TabContainer1.Controls.Count < m + 1) // TabContainer可能包含有TabPanel,通常放置TabContainer控件后,会有1个    {        panel = new AjaxControlToolkit.TabPanel(); // 需要添加新的TabPanel        TabContainer1.Controls.Add(panel);    }    else        panel = TabContainer1.Tabs[m]; // 使用已经存在的TabPanel    panel.HeaderText = sheet_node_list[m].Attributes["name"].Value; // 将TabPanel的标签设置为工作表的名字    panel.ID = "Panel" + m; // 设置ID    panel.Attributes.Add("Width", sheet_node_list[m].Attributes["width"].Value); // 关于表格尺寸的获取,后面再说    panel.Attributes.Add("Height", sheet_node_list[m].Attributes["height"].Value);     int _rowNum = Math.Max(1, int.Parse(sheet_node_list[m].Attributes["row"].Value) + 1); // 获取表格最大行列数    int _colNum = Math.Max(1, int.Parse(sheet_node_list[m].Attributes["col"].Value) + 1);    // create table    Table tbl = new Table(); // 我们的表格,总是生成新滴    tbl.ID = panel.HeaderText + "_Table"; // 设置ID,其客户端ClintID将是ctrl_xxx_yy_工作表名_Table的形式,JS脚本将会用到    tbl.Attributes.Add("activeCell", "null"); // 当前活动的单元格,用于JS脚本    tbl.Attributes.Add("cellspacing", "0"); // 设置表格的界面表现格式    tbl.Attributes.Add("cellpadding", "0");    tbl.BorderColor = System.Drawing.Color.AliceBlue; // 加个边框    tbl.BorderStyle = BorderStyle.Solid;    tbl.BorderWidth = 1;    createTableHeader(tbl, _rowNum, _colNum);   // 创建表头,模拟Excel的行列标识    fillTableData(tbl, _rowNum, _colNum, sheet_node_list[m]); //填充单元格数据    panel.Controls.Add(tbl); //将表格添加到TabPanel中}

 最后在浏览器中显示的结果如下图所示:

 

 现在来看看createTableHeader()方法,该方法生成表头:

protected void createTableHeader(Table tbl, int _rowNum, int _colNum){    bool isHeaderCell = false; // 头部标志    // fill header    for (int r = 0; r < _rowNum; r++) // 遍历行    {       TableRow row = new TableRow(); // 生成行        for (int c = 0; c < _colNum; c++) // 遍历列        {            if ( c == 0 || r == 0 )            {                TableCell cell = new TableCell(); // 生成单元格                  isHeaderCell = false; // 初始化标志                  cell.Style.Add("border-left", "1px solid black"); // 设置格式                  cell.Style.Add("border-top", "1px solid black");                cell.Attributes.Add("width", "83pt"); // 默认宽度                  if ( c == 0 ) // 0列?                  {                    if (r == 0) // 0行?                       {                        cell.Text = " "; // 填个空格                           isHeaderCell = true; // 设置标志                       }                    else                    {                        cell.Text = r.ToString(); // 0列从1-N行表头,填行号                           isHeaderCell = true; // 设置标志                           if (r == _rowNum - 1) cell.Style.Add("border-bottom", "1px solid black"); // 最后一行,画底线                      }                }                else                {                    if ( r == 0 ) // N列(N大于0)0行,列头                       {                        cell.Text = getColLetter(c - 1); // 根据列号生成A-IV的列标识                           isHeaderCell = true; // 设置标志                       }                }                              if ( isHeaderCell ) // 设置表头格式:居中,silver色为底                  {                    cell.Attributes.Add("align", "center");                    cell.Style.Add("background-Color", "silver");                    row.Cells.Add(cell); // 添加单元格到行对象中                  }            }        }                row.Style.Add("border", "1px solid black"); // 设置行边框         tbl.Rows.Add(row); // 将行添加到列    }}

 其中用到的从列号生成A-IV型的Excel列标识的方法,作为课后作业,请同学自行完成。

 生成表头后,再向里面填充数据:

 

protected void fillTableData(Table tbl, int _rowNum, int _colNum, XmlNode  sheet_node){    tbl.Attributes.Add("cellpadding", "0"); // 添加表格属性     tbl.Attributes.Add("cellspacing", "0");     tbl.Style.Add("border-collapse", "collapse"); // 设置表格样式    tbl.Style.Add("table-layout", "fixed");    tbl.Style.Add("width", sheet_node.Attributes["width"].Value + "pt"); // 关于如何取得工作表尺寸的方法,后文再叙    tbl.Style.Add("border", "1px solid black"); // 黑色边框    foreach (XmlNode row_node in sheet_node.ChildNodes) // 遍历行    {        int rowIdx = int.Parse(row_node.Attributes["idx"].Value); //获取行号         tbl.Rows[rowIdx].Attributes.Add("height", row_node.Attributes["height"].Value); // 获取行高               if (row_node.ChildNodes.Count == 0) // 空行?        {           for (int i = 1; i <= _colNum - 1; i++) // 添加空单元格            {                tbl.Rows[rowIdx].Cells.Add(createEmptyCell());           }        }        else        foreach (XmlNode cell_node in row_node.ChildNodes) // 遍历行的单元格         {            int colIdx = int.Parse(cell_node.Attributes["col"].Value); // 列号            int colSpan = int.Parse(cell_node.Attributes["colspan"].Value); // 列合并的跨度            int rowSpan = int.Parse(cell_node.Attributes["rowspan"].Value); // 行合并的跨度            bool hasFormula = bool.Parse(cell_node.Attributes["hasFormula"].Value); // 包含公式?            if (cell_node.PreviousSibling != null) // 是不是第一个有数据的单元格?            {              // 如果相邻两个<CELL>不连续,需要填充中间部分                int prevColIdx = int.Parse(cell_node.PreviousSibling.Attributes["col"].Value), // 前一单元格的列号和列跨度                    prevColSpan = int.Parse(cell_node.PreviousSibling.Attributes["colspan"].Value);                if (prevColIdx + prevColSpan < colIdx) // 如果存在空档,则填充之                  {                    for (int i = prevColIdx + prevColSpan; i < colIdx; i++)                    {                        tbl.Rows[rowIdx].Cells.Add(createEmptyCell()); // 添加空单元格                       }                }            }            else            {                if (colIdx > 1) // 如果起始有数据的单元格不是第一列,则需要填充空白部分                {                    for (int i = 1; i < colIdx; i++)                    {                        tbl.Rows[rowIdx].Cells.Add(createEmptyCell());                    }                }            }            // 现在是戏肉部分,添加有数据的单元格             {                TableCell cell = new TableCell();                // 设置字体样式                  cell.Style.Add("font-family", cell_node.Attributes["font-name"].Value);                cell.Style.Add("color", cell_node.Attributes["font-color"].Value);                cell.Style.Add("font-size", cell_node.Attributes["font-size"].Value);                cell.Style.Add("border", "1px solid black");                cell.Style.Add("width", cell_node.Attributes["width"].Value);                // 设置尺寸                  cell.Attributes.Add("width", cell_node.Attributes["width"].Value);                cell.Attributes.Add("height", cell_node.Attributes["height"].Value);                // 设置对齐方式                  cell.Attributes.Add("align", cell_node.Attributes["align"].Value);                cell.Attributes.Add("valign", cell_node.Attributes["valign"].Value);                // 设置行列跨度                                  // 注意:没有直接设置rowspan属性,而是交给客户端脚本完成                  cell.Attributes.Add("_rowspan", cell_node.Attributes["rowspan"].Value);                cell.Attributes.Add("colspan", cell_node.Attributes["colspan"].Value);                                // 添加数据域                  cell.Attributes.Add("hasFormula", cell_node.Attributes["hasFormula"].Value);                cell.Attributes.Add("formula", cell_node.Attributes["formula"].Value);                cell.Attributes.Add("dataField", hasFormula ? cell_node.Attributes["formula"].Value : cell_node.Attributes["value2"].Value);                cell.Attributes.Add("value2", cell_node.Attributes["value2"].Value); // 使用value2,而不用value,避免与HTML元素属性的冲突                  if (hasFormula) cell.Attributes.Add("title", cell_node.Attributes["formula"].Value); // 添加悬停时的提示,这里只针对公式,也可以是普通单元格                  cell.Text = cell_node.Attributes["value2"].Value; // 设置显示的文本                  tbl.Rows[rowIdx].Cells.Add(cell); // 添加到行            }           // 如果没有后续,需要补完,直到列尾            if (cell_node.NextSibling == null && colIdx + colSpan < _colNum - 1)           {                for (int i = colIdx + 1; i <= _colNum - 1; i++)                {                    tbl.Rows[rowIdx].Cells.Add(createEmptyCell());                }            }        }// for each cell    } // for each row}

 生成空白单元格的方法实现如下:

 

protected TableCell createEmptyCell(){    TableCell cell = new TableCell();    cell.Style.Add("border", "1px solid black"); // 设置样式    cell.Attributes.Add("hasFormula", "false"); // 设置数据域    cell.Attributes.Add("formula", "");    cell.Attributes.Add("dataField", "");    cell.Attributes.Add("value2", "");    cell.Text = " "; // 设置显示的文本    return cell;}

 现在runat="server"端的表格生成工作已经完成,整个表格已经初具雏形,除了行跨度还有问题外。

通过以下脚本,可以解决行跨度问题。本节标题为“服务器端生成”,照理不应该使用客户端脚本,这里只是描述客户端如何实现相应的操作,理论上在服务器端也可以完成相当的工作:

 

for( var i = tbl.cells.length - 1; i > 1; i-- ) // 从后向前遍历行{    var ctrl = tbl.cells[i]; // 单元格对象    if ( ctrl.cellIndex > 0 && ctrl.parentElement.rowIndex > 0) // 0列0行不用处理    {        if ( ctrl._rowspan && ctrl._rowspan > 1 && // 跨度>1并且范围没有超过表格               ctrl.parentElement.rowIndex + 1 < tbl.rows.length )        {            for( var n = 1; n < ctrl._rowspan; n++ ) // 合并单元格              {                if ( ctrl.cellIndex < tbl.rows[ctrl.parentElement.rowIndex + 1].cells.length ) // 合并一次就判断是否超界                    tbl.rows[ctrl.parentElement.rowIndex + 1].deleteCell(ctrl.cellIndex); // 删除下一行同一列的单元格              }            ctrl.rowSpan = ctrl._rowspan; // 最后设置单元格的行跨度        }    }}

 为什么要从后向前遍历行呢?因为当删除一个单元格时,后面的单元格会向前移,倒序处理可以避免这种错位问题。