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

读写数据都完成了,现在来看看如何生成客户端界面。使用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; // 最后设置单元格的行跨度
        }
    }
}

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

 

你可能感兴趣的:(html,服务器,Excel,table,asp.net,border)