GridView合并单元格及导出至Excel

#region GridView合并矩形单元格
    /// <summary>
    /// 合并成为矩形单元格
    /// </summary>
    /// <param name="GridView1"></param>
    /// <param name="sRow">开始行索引</param>
    /// <param name="sCol">开始列索引</param>
    /// <param name="eRow">结束行索引</param>
    /// <param name="eCol">结束列索引</param>
    public static void GroupRectangle(GridView GridView1, int sRow, int sCol, int eRow, int eCol)
    {
        if (sRow < 0 || sRow > eRow || eRow > GridView1.Rows.Count - 1)
        {
            return;
        }
        if (sCol < 0 || sCol > eCol || eCol > GridView1.Columns.Count - 1)
        {
            return;
        }
        TableCell oldTc = GridView1.Rows[sRow].Cells[sCol];
        oldTc.ColumnSpan = eCol - sCol + 1;
        oldTc.RowSpan = eRow - sRow + 1;
        //oldTc.ColumnSpan = eCol - sCol > 0 ? eCol - sCol : 1;
        //oldTc.RowSpan = eRow - sRow > 0 ? eRow - sRow : 1;
        for (int i = sRow; i <= eRow; i++)
        {
            for (int j = sCol; j <= eCol; j++)
            {
                if (i == sRow && j == sCol)//起始单元格
                {
                    //oldTc.ColumnSpan = 1;
                    //oldTc.RowSpan = 1;
                    continue;
                }

                TableCell tc = GridView1.Rows[i].Cells[j];
                tc.Visible = false;
            }
        }
        oldTc.Visible = true;
        oldTc.VerticalAlign = VerticalAlign.Middle;
        oldTc.HorizontalAlign = HorizontalAlign.Center;
    }
    #endregion

 

所有生成的Excel文件均包含了单元格合并的信息

#region GridView数据导出为,该方法未导出的Excel与纯正Excel相差较大,且在数据量较大时会出现内存溢出

    /// <summary>
    /// XmlDocument转换xml文件
    /// </summary>
    /// <param name="gv"></param>
    /// <param name="filePath">Server.MapPath("bookstore.xml")</param>
    public void ToXMLMethod1(GridView gv, string filePath)
    {
        XmlText xmlText;
        XmlDocument xmlDoc = new XmlDocument();

        //加入XML的声明段落
        XmlNode xmlNode = xmlDoc.CreateXmlDeclaration("1.0", "UTF-8", null);
        xmlDoc.AppendChild(xmlNode);

        //添加一个根元素
        XmlElement xmlTable = xmlDoc.CreateElement("", "table", string.Empty);
        xmlText = xmlDoc.CreateTextNode("");
        xmlTable.AppendChild(xmlText);
        xmlDoc.AppendChild(xmlTable);


        //添加了自定义表头
        if (gv.HeaderRow.Cells.Count > 0)
        {
            TableCellCollection cellCollection = gv.HeaderRow.Cells;


            XmlElement xmlHeader = xmlDoc.CreateElement("", "tr", string.Empty);
            xmlText = xmlDoc.CreateTextNode("");
            xmlHeader.AppendChild(xmlText);
            xmlDoc.ChildNodes.Item(1).AppendChild(xmlHeader);
            for (int i = 0; i < gv.HeaderRow.Cells.Count; i++)
            {
                XmlElement xmlTd = xmlDoc.CreateElement("", "td", string.Empty);
                xmlText = xmlDoc.CreateTextNode(cellCollection[i].Text);
                xmlTd.AppendChild(xmlText);
                xmlTd.SetAttribute("RowSpan", gv.HeaderRow.Cells[i].RowSpan.ToString());
                xmlTd.SetAttribute("ColumnSpan", gv.HeaderRow.Cells[i].ColumnSpan.ToString());
                xmlDoc.ChildNodes.Item(1).AppendChild(xmlHeader).AppendChild(xmlTd);
            }
        }



        for (int i = 0; i < gv.Rows.Count; i++)
        {
            //加入子元素tr
            XmlElement xmlTr = xmlDoc.CreateElement("", "tr", string.Empty);
            xmlText = xmlDoc.CreateTextNode("");
            xmlTr.AppendChild(xmlText);
            xmlDoc.ChildNodes.Item(1).AppendChild(xmlTr);

            for (int j = 0; j < gv.Rows[i].Cells.Count; j++)
            {
                TableCell cell = gv.Rows[i].Cells[j];

                XmlElement xmlTd = xmlDoc.CreateElement("", "td", string.Empty);
                xmlText = xmlDoc.CreateTextNode(cell.Text);
                xmlTd.AppendChild(xmlText);
                xmlTd.SetAttribute("RowSpan", cell.RowSpan.ToString());
                xmlTd.SetAttribute("ColumnSpan", cell.ColumnSpan.ToString());
                xmlDoc.ChildNodes.Item(1).AppendChild(xmlTr).AppendChild(xmlTd);
            }
        }

        xmlDoc.Save(filePath); //保存 
    }
    #endregion

 

为改善上一段代码出现的内存溢出问题,修改为每次添加语句直接写入XML文件

#region 将GridView中的数据导出为Xml文件

    /// <summary>
    /// 将GridView中的数据导出为Xml文件
    /// </summary>
    /// <param name="gv">需要处理的GridView</param>
    /// <param name="filePath">xml文件存放路径</param>
    public static void ToXml(GridView gv, string filePath)
    {

        FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite);
        StreamWriter sw = new StreamWriter(fs);

        #region 添加xml头 并指定excel打开

        string xmlStr = "<?xml version='1.0'?>" +//xml头文件
"<?mso-application progid='Excel.Sheet'?>" +//默认以Excel打开
"<Workbook xmlns='urn:schemas-microsoft-com:office:spreadsheet' " +
 "xmlns:o='urn:schemas-microsoft-com:office:office' " +
 "xmlns:x='urn:schemas-microsoft-com:office:excel' " +
 "xmlns:ss='urn:schemas-microsoft-com:office:spreadsheet' " +
 "xmlns:html='http://www.w3.org/TR/REC-html40'>";

        xmlStr += "<DocumentProperties xmlns='urn:schemas-microsoft-com:office:office'></DocumentProperties>" +
 "<OfficeDocumentSettings xmlns='urn:schemas-microsoft-com:office:office'></OfficeDocumentSettings>" +
 "<ExcelWorkbook xmlns='urn:schemas-microsoft-com:office:excel'></ExcelWorkbook>" +
 "<Styles> " +
 "<Style ss:ID='Default' ss:Name='Normal'>" +
     "<Alignment ss:Vertical='Center' ss:Horizontal='Center'/>" +
        "<Borders/>" +
        "<Font ss:FontName='宋体' x:CharSet='134' ss:Size='11' ss:Color='#000000'/><Interior/>" +
   "<NumberFormat/>" +
   "<Protection/>" +
  "</Style>" +
      "</Styles>";


        sw.Write(xmlStr);
        xmlStr = string.Empty;

        #endregion

        //新建工作表
        xmlStr += "<Worksheet ss:Name='Sheet1'>";
        //新建表格
        xmlStr += "<Table x:FullColumns='1' x:FullRows='1' ss:DefaultColumnWidth='54' ss:DefaultRowHeight='13.5'>";

        sw.WriteLine(xmlStr);
        xmlStr = string.Empty;

        /***************************************/
        /***************添加表头****************/
        /***************************************/
        string a1 = "";
        xmlStr += "<Row>";
        for (int i = 0; i < gv.HeaderRow.Cells.Count; i++)
        {
            a1 += "<tr><th>" + gv.HeaderRow.Cells[i].Text;
            xmlStr += "<Cell ";
            TableCell cell = gv.HeaderRow.Cells[i];

            //跨列属性可能为添加了html属性colspan,也可能是由cell的ColumnSpan属性指定
            int colSpan = -1;
            if (cell.Attributes["colspan"] != null && cell.Attributes["colspan"] != string.Empty)
            {
                colSpan = int.Parse(cell.Attributes["colspan"].ToString());
            }
            if (cell.ColumnSpan > 1)
            {
                colSpan = cell.ColumnSpan;
            }
            if (colSpan > 0)
            {
                colSpan--;
                xmlStr += "ss:MergeAcross='" + colSpan.ToString() + "'";
            }
            xmlStr += " ><Data ss:Type='String'>";
            string strHeader = gv.HeaderRow.Cells[i].Text;

            if (strHeader.Contains("</th></tr><tr>"))//换行标记
            {
                xmlStr += strHeader.Substring(0, strHeader.Length - 14) + "</Data></Cell></Row><Row>";
            }
            else
            {
                xmlStr += strHeader + "</Data></Cell>";
            }
        }
        xmlStr += "</Row>";


        sw.WriteLine(xmlStr);
        xmlStr = string.Empty;

        /***************************************/
        /***************添加数据****************/
        /***************************************/

        int rowCount = gv.Rows.Count;

        //gv.HeaderRow.Cells.Count属性始终为0,可能为控件bug。循环控件表头遍历出列数
        int colCOunt = 0;
        foreach (TableCell cell in gv.HeaderRow.Cells)
        {
            if (cell.Text.Contains("</th></tr><tr>"))
            {
                colCOunt = 0;
                continue;
            }
            else
            {
                colCOunt++;
            }
        }

        for (int i = 0; i < rowCount; i++)
        {
            xmlStr += "<Row>";

            bool flagPreviousVisible = true;
            for (int j = 0; j < colCOunt; j++)
            {
                if (gv.Rows[i].Cells[j].Visible == false)
                {
                    flagPreviousVisible = false;
                    continue;
                }

                #region 判断是否为矩形单元格中的单元格

                if (i > 0 && j > 0)
                {
                    bool flagSkip = false;
                    int tempx = -1;//记录矩形单元格的左下角位置
                    for (int l = j - 1; l >= 0; l--)
                    {
                        if (gv.Rows[i].Cells[l].Visible == false)
                        {
                            tempx = l;
                            break;
                        }
                        else
                        {
                            continue;
                        }
                    }
                    if (tempx > -1)//存在可能的矩形单元格
                    {
                        for (int m = i - 1; m >= 0; m--)
                        {
                            int colSpan = j - tempx + 1;
                            int rowSpan = i - m + 1;
                            if (gv.Rows[m].Cells[tempx].ColumnSpan >= colSpan && gv.Rows[m].Cells[tempx].RowSpan >= rowSpan)//存在矩形单元格,即当前单元格属于矩形单元格中的一部分
                            {
                                flagPreviousVisible = false;//模拟前一单元格不可见(为添加Index属性)
                                flagSkip = true;
                                break;//满足条件,跳出循环
                            }
                            else
                            {
                                continue;
                            }
                        }
                    }
                    if (flagSkip)//存在矩形单元格,且该单元格存在于该矩形单元格,跳过此单元格拼接字符串
                    {
                        continue;
                    }
                }

                #endregion

                int MergeAcross = gv.Rows[i].Cells[j].ColumnSpan;//跨列,即合并的列数
                int MergeDown = gv.Rows[i].Cells[j].RowSpan;//跨行,即合并的行数

                xmlStr += "<Cell ";

                if (MergeAcross > 0)//存在要合并的行
                {
                    xmlStr += "ss:MergeAcross='" + --MergeAcross + "' ";//ss:MergeAcross 合并列 
                }
                if (MergeDown > 0)//存在要合并的列
                {
                    xmlStr += "ss:MergeDown='" + --MergeDown + "' ";//ss:MergeDown 合并行
                }


                if (flagPreviousVisible == false)
                {
                    int a = j + 1;
                    xmlStr += "ss:Index='" + a + "' ";
                    flagPreviousVisible = true;
                }
                xmlStr += " >";
                xmlStr += "<Data ss:Type='String'>" + gv.Rows[i].Cells[j].Text + "</Data>";//
                xmlStr += "</Cell>";
            }
            xmlStr += "</Row>";

            sw.WriteLine(xmlStr);
            xmlStr = string.Empty;
        }

        xmlStr += "</Table>";

        sw.WriteLine(xmlStr);
        xmlStr = string.Empty;

        #region 添加xml打印格式等属性,完成xml字符串拼接

        //WorksheetOptions主要定义 打印方式  边距 窗口冻结 等等 ,在设计 Excel文件时都设置好了  不需要修改
        xmlStr += "<WorksheetOptions xmlns='urn:schemas-microsoft-com:office:excel'>" +
   "<PageSetup>" +
    "<Header x:Margin='0.3'/>" +
    "<Footer x:Margin='0.3'/>" +
    "<PageMargins x:Bottom='0.75' x:Left='0.7' x:Right='0.7' x:Top='0.75'/>" +
   "</PageSetup>" +
   "<Print>" +
    "<ValidPrinterInfo/>" +
    "<PaperSizeIndex>9</PaperSizeIndex>" +
    "<HorizontalResolution>200</HorizontalResolution>" +
    "<VerticalResolution>200</VerticalResolution>" +
   "</Print>" +
   "<Selected/>" +
            /*
             * 
             *这段代码加载<Selected>标签之后,用于冻结窗口
             * 
             * <FreezePanes/>
             * <FrozenNoSplit/>
             * <SplitHorizontal>3</SplitHorizontal>//冻结的窗口行数             
             * <ActivePane>2</ActivePane>
             *                                        
             */

   "<Panes>" +
    "<Pane>" +
     "<Number>3</Number>" +
     "<ActiveRow>2</ActiveRow>" +
     "<RangeSelection>R3C1:R4C1</RangeSelection>" +
    "</Pane>" +
   "</Panes>" +
   "<ProtectObjects>False</ProtectObjects>" +
   "<ProtectScenarios>False</ProtectScenarios>" +
  "</WorksheetOptions>";
        xmlStr += "</Worksheet>";
        xmlStr += "</Workbook>";

        #endregion

        sw.Write(xmlStr);
        xmlStr = string.Empty;

        sw.Close();
        fs.Close();

        //XmlDocument xmldoc = new XmlDocument(); //创建空的XML文档
        //xmldoc.LoadXml(xmlStr);
        //xmldoc.Save(Server.MapPath("temp.xml")); //保存 
    }


    #endregion

使用第三方插件NPOI生成真正Excel文件

#region 将GridView中的数据导出为Xml文件 表头部分待完善

    /// <summary>
    /// 将GridView中的数据导出为Xml文件
    /// </summary>
    /// <param name="gv">需要处理的GridView</param>
    /// <param name="filePath">xml文件存放路径</param>
    public static void ToXml(GridView gv, string filePath)
    {
        IWorkbook hssfworkbook = new HSSFWorkbook();
        ISheet sheet = hssfworkbook.CreateSheet("sheet1");

        IRow row = sheet.CreateRow(0);
        ICell cell;

        ICellStyle cellStyle = hssfworkbook.CreateCellStyle();
        cellStyle.Alignment = HorizontalAlignment.CENTER_SELECTION;
        cellStyle.VerticalAlignment = VerticalAlignment.CENTER;

        int colCount = 0;//记录GridView列数
        int rowInex = 0;//记录表头的行数

        /***************************************/
        /***************添加表头****************/
        /***************************************/

        #region 添加表头

        int groupCount = 0;//记录分组数
        int colIndex = 0;//记录列索引,并于结束表头遍历后记录总列数
        for (int i = 0; i < gv.HeaderRow.Cells.Count; i++)
        {
            if (rowInex != groupCount)//新增了标题行时重新创建
            {
                row = sheet.CreateRow(rowInex);
                groupCount = rowInex;
            }

            #region 是否跳过当前单元格

            for (int m = 0; m < sheet.NumMergedRegions; m++)//遍历所有合并区域
            {
                NPOI.SS.Util.CellRangeAddress a = sheet.GetMergedRegion(m);
                //当前单元格是处于合并区域内
                if (a.FirstColumn <= colIndex && a.LastColumn >= colIndex
                    && a.FirstRow <= rowInex && a.LastRow >= rowInex)
                {
                    colIndex++;
                    m = 0;//重新遍历所有合并区域判断新单元格是否位于合并区域
                }
            }
            

            #endregion

            cell = row.CreateCell(colIndex);
            cell.CellStyle = cellStyle;

            TableCell tablecell = gv.HeaderRow.Cells[i];

            //跨列属性可能为添加了html属性colspan,也可能是由cell的ColumnSpan属性指定
            int colSpan = 0;
            int rowSpan = 0;

            #region 获取跨行跨列属性值
            //跨列
            if (!string.IsNullOrEmpty(tablecell.Attributes["colspan"]))
            {
                colSpan = int.Parse(tablecell.Attributes["colspan"].ToString());
                colSpan--;
            }
            if (tablecell.ColumnSpan > 1)
            {
                colSpan = tablecell.ColumnSpan;
                colSpan--;
            }

            //跨行
            if (!string.IsNullOrEmpty(tablecell.Attributes["rowSpan"]))
            {
                rowSpan = int.Parse(tablecell.Attributes["rowSpan"].ToString());
                rowSpan--;
            }
            if (tablecell.RowSpan > 1)
            {
                rowSpan = tablecell.RowSpan;
                rowSpan--;
            }
            #endregion

            //添加excel合并区域
            if (colSpan > 0 || rowSpan > 0)
            {
                sheet.AddMergedRegion(new NPOI.SS.Util.CellRangeAddress(rowInex, rowInex + rowSpan, colIndex, colIndex + colSpan));
                colIndex += colSpan + 1;//重新设置列索引
            }
            else
            {
                colIndex++;
            }
            string strHeader = gv.HeaderRow.Cells[i].Text;

            if (strHeader.Contains("</th></tr><tr>"))//换行标记,当只存在一行标题时不存在</th></tr><tr>,此时colCount无法被赋值
            {
                rowInex++;
                colCount = colIndex;
                colIndex = 0;

                strHeader = strHeader.Substring(0, strHeader.IndexOf("</th></tr><tr>"));
            }
            cell.SetCellValue(strHeader);
        }
        if (groupCount == 0)//只有一行标题时另外为colCount赋值
        {
            colCount = colIndex;
        }

        #endregion

        rowInex++;//表头结束后另起一行开始记录控件数据行索引

        /***************************************/
        /***************添加数据****************/
        /***************************************/

        #region 添加数据

        int rowCount = gv.Rows.Count;

        for (int i = 0; i < rowCount; i++)
        {
            row = sheet.CreateRow(rowInex);

            //bool flagPreviousVisible = true;
            for (int j = 0; j < colCount; j++)
            {
                if (gv.Rows[i].Cells[j].Visible == false)
                {
                    //flagPreviousVisible = false;
                    continue;
                }

                #region 判断是否为矩形单元格中的单元格

                if (i > 0 && j > 0)
                {
                    bool flagSkip = false;
                    int tempx = -1;//记录矩形单元格的左下角位置
                    for (int l = j - 1; l >= 0; l--)
                    {
                        if (gv.Rows[i].Cells[l].Visible == false)
                        {
                            tempx = l;
                            break;
                        }
                        else
                        {
                            continue;
                        }
                    }
                    if (tempx > -1)//存在可能的矩形单元格
                    {
                        for (int m = i - 1; m >= 0; m--)
                        {
                            int colSpan = j - tempx + 1;
                            int rowSpan = i - m + 1;
                            if (gv.Rows[m].Cells[tempx].ColumnSpan >= colSpan && gv.Rows[m].Cells[tempx].RowSpan >= rowSpan)//存在矩形单元格,即当前单元格属于矩形单元格中的一部分
                            {
                                flagSkip = true;
                                break;//满足条件,跳出循环
                            }
                            else
                            {
                                continue;
                            }
                        }
                    }
                    if (flagSkip)//存在矩形单元格,且该单元格存在于该矩形单元格,跳过此单元格拼接字符串
                    {
                        continue;
                    }
                }

                #endregion


                cell = row.CreateCell(j);
                cell.CellStyle = cellStyle;
                cell.SetCellType(CellType.STRING);
                if (!string.IsNullOrEmpty(gv.Rows[i].Cells[j].Text))
                {

                    cell.SetCellValue(gv.Rows[i].Cells[j].Text);
                }
                else
                {
                    cell.SetCellValue(" ");
                }


                int MergeAcross = gv.Rows[i].Cells[j].ColumnSpan;//跨列,即合并的列数
                if (MergeAcross > 0)
                {
                    MergeAcross--;
                }
                else
                {
                    MergeAcross = 0;
                }
                //? MergeAcross = MergeAcross - 1 : MergeAcross = 0;
                int MergeDown = gv.Rows[i].Cells[j].RowSpan;//跨行,即合并的行数
                //MergeDown > 0 ? MergeDown-- : MergeDown = 0;
                if (MergeDown > 0)
                {
                    MergeDown--;
                }
                else
                {
                    MergeDown = 0;
                }

                if (MergeAcross > 0 || MergeDown > 0)//存在要合并的行
                {
                    sheet.AddMergedRegion(new NPOI.SS.Util.CellRangeAddress(rowInex, rowInex + MergeDown, j, j + MergeAcross));
                    j += MergeAcross;
                    //colCount += 1;
                }


                FileStream fs = new FileStream(filePath, FileMode.Create);
                hssfworkbook.Write(fs);
                fs.Close();
            }
            rowInex++;
        }

        #endregion
    }


    #endregion     
View Code

 

posted on 2014-07-28 16:58  ChingyZhang  阅读(629)  评论(0编辑  收藏  举报

导航