zoukankan      html  css  js  c++  java
  • c#操作Excel模板,替换命名单元格或关键字形成报表

    c#操作Excel模板,替换命名单元格或关键字形成报表

    http://blog.sina.com.cn/s/blog_45eaa01a0102vqma.html
    一 建立Excel 模板文件 template.xls

    c#操作Excel模板,替换命名单元格或关键字形成报表


    1.1 插入命名单元格的方法:

    左上角名称框,显示当前单元格的行列号C2,加入命名后会显示其命名name

    方法一:

    (1) 点击 单元格“姓名”之后的单元格
    (2) 菜单 插入--名称--定义
    (3) 在框中输入 name

       确保底部的引用位置为 =Users!$C$2
       按“添加”、“确定”按钮 即可

    c#操作Excel模板,替换命名单元格或关键字形成报表

    方法二:
    (1) 点击 单元格“姓名”之后的单元格
    (2) 在左上角名称框中,输入名称即可  

    1.2 制作模板如下:
    (1) 在1行1列,写入序号,在2行1列,插入名称  order_num
    (2) 在1行2列,写入“报告日期”,在1行3列,插入名称  _报告日期
    (3) 在2行2列,写入"姓名",在2行3列,插入名称  name
    (4) 在3行2列,写入"年龄",在3行3列,插入名称  age
    (5) 在3行2列,写入"结论",在3行3列,插入名称  _结论

    c#操作Excel模板,替换命名单元格或关键字形成报表

    二 建立一个,WindowForm格式的解决方案WindowsFormsApplication10

    三 添加对Excel的引用
    1 右击工程的“应用”文件夹--“添加引用”--在“COM”选项页,
      选择“Microsoft Excel 11.0 Object Library”,添加到本工程中,针对office 2003
      并自动引入“Microsoft Office 11.0 Object Library”
    c#操作Excel模板,替换命名单元格或关键字形成报表

    2 在实现的文件中,加入引用语句:using Excel=Microsoft.Office.Interop.Excel;
      按需要,是否加入引用语句:using Microsoft.Office.Core;


    3 注意:如果工程中,曾经加入过Excel,office,VBIDE相同的引用,则再加入后,
    需要从该引用的“属性”中,将“潜入互操作类型”从True 改变为 False,否则无法编译

    c#操作Excel模板,替换命名单元格或关键字形成报表

    四 建立一个操作EXCEL的类ExcelTemplate,并在实际工程中,创建类对象即可。
       特别注意:为了程序自动处理方便起见,命名单元格的规则如下:
       1.1 开头处的命名单元格,以1个下划线开始,比如,_报告日期
       1.2 中间多行组循环的命名单元格,不加下划线,且与数据集的字段名一致为好
       1.3 结尾处的命名单元格,以2个下划线开头,比如,__合计

    五 结果如下:

    c#操作Excel模板,替换命名单元格或关键字形成报表

    六 完整代码如下:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    //1 右击工程的“应用”文件夹--“添加引用”--在“COM”选项页,
    //  选择“Microsoft Excel 11.0 Object Library”,添加到本工程中,针对office 2003
    //  并自动引入“Microsoft Office 11.0 Object Library”
    //2 在实现的文件中,加入引用语句:using Excel=Microsoft.Office.Interop.Excel;
    //  按需要,是否加入引用语句:using Microsoft.Office.Core;
    using Excel = Microsoft.Office.Interop.Excel;
    using Microsoft.Office.Core;
    namespace WindowsFormsApplication10
    {
    //一 主界面文件
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }

            private void button1_Click(object sender, EventArgs e)
            {//这里演示打开模板文件,替换后保存成实际数据的文件
                //1 定位Excel模板文件template.xls
                string str;
                str = Application.StartupPath;//工作路径
                //       str = Application.ExecutablePath;
                //       str = Environment.CurrentDirectory;
                //       str = System.IO.Directory.GetCurrentDirectory();
                //       str=AppDomain.CurrentDomain.BaseDirectory;
                //d:WindowsFormsApplication10WindowsFormsApplication10inDebug
                //d:WindowsFormsApplication10WindowsFormsApplication10 emplate.xls
                int pos = -1;
                pos = str.IndexOf("\bin\Debug");
                string strPath = str;
                if (pos > 0) strPath = str.Substring(0, pos);
                else
                {
                    pos = str.IndexOf("\bin\Release");
                    if (pos > 0) strPath = str.Substring(0, pos);
                }
                str = strPath + "\template.xls";
               
                //2 生成模板类的对象 ,并打开模板文件
                ExcelTemplate  em = new ExcelTemplate ();
                em.Open(str);// ("c:\d.xls");//"template.xls"

                //3 这里简单生成样例数据表,工作中要以实际的数据集为准
                DataTable dt = new DataTable();
                dt.Columns.Add("name", typeof(string));
                dt.Columns.Add("age", typeof(int));

                DataRow dr = dt.NewRow();
                dr["name"] = "张三"; dr["age"] = 20;
                dt.Rows.Add(dr);

                dr = dt.NewRow();
                dr["name"] = "李四"; dr["age"] = 25;
                dt.Rows.Add(dr);

                //4调用模板替换函数,生成实际的数据
                em.SetNameCellValue(dt);

                //5 按当天日期 存盘
                string execlName = strPath + "" + DateTime.Now.ToString("yyyyMMdd-hhmmss") + ".xls";
                em.SaveAs(execlName);

                //6 退出
                em.Close();
                MessageBox.Show(execlName + "     生成成功!");
               

            }
        }

    //二 打开Excel模板的类,替换命名单元格的数据,生成实际的数据结果文件
        public class ExcelTemplate
        {
            public string mFilename;
            public Excel.Application app;
            public Excel.Workbooks wbs;
            public Excel.Workbook wb;
            public Excel.Worksheets wss;
            public Excel.Worksheet ws;
            public ExcelTemplate()
            {
                //
                // TODO: 在此处添加构造函数逻辑
                //
            }
            public void Create()//创建一个Excel对象
            {
                app = new Excel.Application();
                wbs = app.Workbooks;
                wb = wbs.Add(true);
            }
            public void Open(string FileName)//打开一个Excel文件
            {
                app = new Excel.Application();
                wbs = app.Workbooks;
                wb = wbs.Add(FileName);
                //wb = wbs.Open(FileName,  0, true, 5,"", "", true, Excel.XlPlatform.xlWindows, " ", false, false, 0, true,Type.Missing,Type.Missing);
                //wb = wbs.Open(FileName,Type.Missing,Type.Missing,Type.Missing,Type.Missing,Type.Missing,Type.Missing,Excel.XlPlatform.xlWindows,Type.Missing,Type.Missing,Type.Missing,Type.Missing,Type.Missing,Type.Missing,Type.Missing);
                mFilename = FileName;
            }
            public Excel.Worksheet SetNameValue(string SheetName)//获取一个工作表
            {
                Excel.Worksheet s = (Excel.Worksheet)wb.Worksheets[SheetName];
                return s;
            }

            public void SetNameCellValue(DataTable dt)//Excel.Worksheet ws, ws:要设值的工作表   ,数据源表  dt
            {
                Excel.Worksheet ws = (Excel.Worksheet)app.ActiveSheet;
                // ws.Range[ws.Cells[1, 255], ws.Cells[1, 255]].Copy();

                //(一) 这里设置表头的项目,比如报表日期
                //特别注意:为了容易起见,命名单元格的规则如下
                //1.1 开头处的命名单元格,以1个下划线开始,比如,_报告日期
                //1.2 中间循环命名单元格,就是正常的,与数据集的字段名一致为好
                //1.3 结尾处的命名单元格,以2个下划线开头,比如,__合计
                try
                {//为了程序自动处理方便起见
                    app.Goto("_报告日期");
                    app.ActiveCell.FormulaR1C1 = DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss");
                    //str = app.ActiveCell.Value.ToString();//ws.Evaluate[titleName]
                }
                catch (System.Exception ex)
                {

                }

                //(二) 根据数据源的个数,设置重复变化的数据行组,
                //1 声明与命名单元格相关的变量和数组
                int min_Row = 65536, min_COl = 65536, max_Row = 0;
                string min_Name = "";
                int nameCellCount = app.ActiveWorkbook.Names.Count;//获得命名单元格的总数
                int[] nameCellRow = new int[nameCellCount];//某个命名单元格的行
                int[] nameCellColumn = new int[nameCellCount];//某个命名单元格的列
                string[] nameCellTag = new string[nameCellCount];//某个命名单元格的常规地址 ,比如 $A$1
                string[] nameCellName = new string[nameCellCount];//某个命名单元格的自定义名称,比如 工资

                string strName, str;
                int row1, row2;//选择重复的行
                int nameCellIdx = 0;

                //2 寻找所有命名的单元格,并找到行号最小者,以便在它之前插入循环行
                for (int k = 0; k < nameCellCount; k++)
                {
                    strName = app.ActiveWorkbook.Names.Item(k + 1).Name;
                    str = strName.Substring(0, 1);
                    if (str == "_")
                    {
                        continue;//如果第一个字符为下划线,则认为是固定命名单元格,不是命名的循环单元格
                    }

                    app.Goto(strName);
                    nameCellColumn[nameCellIdx] = app.ActiveCell.Column;
                    nameCellRow[nameCellIdx] = app.ActiveCell.Row;
                    nameCellName[nameCellIdx] = strName;

                    nameCellTag[nameCellIdx] = app.ActiveCell.Address;//$A$1
                    nameCellTag[nameCellIdx] = nameCellTag[nameCellIdx].Split('$')[1];//$A$1--> A

                    if (min_Row > nameCellRow[nameCellIdx])
                    {
                        min_Row = nameCellRow[nameCellIdx];//最小的行号
                        min_COl = nameCellColumn[nameCellIdx];
                        min_Name = nameCellName[nameCellIdx];

                    }
                    if (max_Row < nameCellRow[nameCellIdx]) max_Row = nameCellRow[nameCellIdx]; ;//最大行号
                    nameCellIdx++;//真实的循环的命名单元格序号

                }
                nameCellCount = nameCellIdx;//实际要处理的循环的命名单元格数目
                int loopRow = max_Row - min_Row + 1;//一次循环的函数
                //3 也可以使用 foreach ( Excel.Name  nn in app.ActiveWorkbook.Names)MessageBox.Show(nn.Name);
     
                //4 根据数据集的实际数据行数,查找命名单元格,循环插入数据
                for (int dt_row_idx = 0; dt_row_idx < dt.Rows.Count; dt_row_idx++)
                {//循环实际的数据行数

                    //4.1 找到行号最小的循环行组的命名单元格,以它来定位
                    app.Goto(min_Name);

                    //4.2 //插入循环重复的摸板行组的行,使得所有命名单元格都向后移,以便下次循环查找定位使用
                    for (int j = 0; j < loopRow; j++)
                    {//插入需要重复循环的行数loopRow的空行
                        app.ActiveCell.EntireRow.Insert();
                    }

                    //4.3 定位到摸板行组首行
                    app.Goto(min_Name);//转到摸板行组的行号最小的命名单元格,以它来定位
                    row1 = app.ActiveCell.Row;  //摸板行组的第一行
                    row2 = row1 + loopRow - 1; //摸板行组的最后一行

                    //4.4 复制整体模板的多行组,固定的摸板的格式和相关的文字说明,也可一个一个单元格复制
                    ws.Range[ws.Cells[row1, 1], ws.Cells[row2, 255]].Copy();//整体多行组复制摸板行组

                    //4.5 定位到新加入行的第一个单元格内
                    row1 = row1 - loopRow;//向上回退到新加入的行组
                    row2 = row2 - loopRow;
     
                    //4.6 粘贴整体多行组,固定的摸板的格式和相关的文字说明
                    ws.Range[ws.Cells[row1, 1], ws.Cells[row2, 255]].PasteSpecial();//整体多行组粘贴摸板行组

                    //4.7 动态的数值加入
                    for (int name_cell_idx = 0; name_cell_idx < nameCellCount; name_cell_idx++)
                    {//循环命名单元格数量
                        //str = string.Format("{0}{1}", nameCellTag[name_cell_idx], nameCellRow[name_cell_idx]);
                        if (nameCellName[name_cell_idx].ToString() == "order_num")
                        {//序号
                            str = string.Format("{0}", dt_row_idx + 1);
                        }
                        else
                        {//以命名单元格的名称,来取数据表行的对应字段的值
                            str = dt.Rows[dt_row_idx][nameCellName[name_cell_idx]].ToString();

                        }
                        //app.ActiveCell.FormulaR1C1 = str ;
                        ws.Cells[nameCellRow[name_cell_idx], nameCellColumn[name_cell_idx]] = str;
                        nameCellRow[name_cell_idx] += loopRow;//行号增加重复行的个数loopRow,准备下个循环,定位行使用
                    }

                }
                // 5 删除重复的摸板行,不再需要
                app.Goto(min_Name);//找到行号最小的命名单元格,以它来定位
                row1 = app.ActiveCell.Row;
                row2 = row1 + loopRow - 1;//选择重复的行
                ws.Range[ws.Cells[row1, 1], ws.Cells[row2, 255]].Delete();

                //(三) 清除剪贴板,避免Excel关闭工作簿的时候出现提示
                //1 删除剪切板的内容,防止退出提示
                //app.CutCopyMode = flase;//Excel.XlCutCopyMode.xlCut;//删除剪切板的内容,防止退出提示
                //2 直接用range("A1").copy就行,不必把剪贴板都清空,这会影响其他进程的工作的
                ws.Range[ws.Cells[1, 1], ws.Cells[1, 1]].Copy();

                //(四) 结尾方面的工作
                try
                {//为了程序自动处理方便起见
                    app.Goto("__结论");//结尾处的命名单元格,都以2个下划线__开头
                    app.ActiveCell.FormulaR1C1 = "成功完成";
                }
                catch (System.Exception ex)
                {

                }

            }
            public void InsertPictures(string Filename, Excel.Worksheet ws)//插入图片
            {
                ws.Shapes.AddPicture(Filename, MsoTriState.msoFalse, MsoTriState.msoTrue, 10, 10, 150, 150);//后面的数字表示位置
            }
            public void InsertActiveChart(Excel.XlChartType ChartType, Excel.Worksheet ws, int DataSourcesX1, int DataSourcesY1, int DataSourcesX2, int DataSourcesY2, Excel.XlRowCol ChartDataType)//插入图表操作
            {
                ChartDataType = Excel.XlRowCol.xlColumns;
                wb.Charts.Add(Type.Missing, Type.Missing, Type.Missing, Type.Missing);
                {
                    wb.ActiveChart.ChartType = ChartType;
                    wb.ActiveChart.SetSourceData(ws.get_Range(ws.Cells[DataSourcesX1, DataSourcesY1], ws.Cells[DataSourcesX2, DataSourcesY2]), ChartDataType);
                    wb.ActiveChart.Location(Excel.XlChartLocation.xlLocationAsObject, ws);
                }
            }
            public bool SaveAs(object FileName)//文档另存为
            {
                try
                {
                    wb.SaveAs(FileName, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Excel.XlSaveAsAccessMode.xlExclusive, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);
                    return true;

                }
                catch (Exception ex)
                {
                    return false;

                }
            }
            public void Close()//关闭一个Excel对象,销毁对象
            {
                wb.Close(Type.Missing, Type.Missing, Type.Missing);
                wbs.Close();
                app.Quit();
                wb = null;
                wbs = null;
                app = null;
                GC.Collect();
            }
        }
    //==
    }

    ===============222222=======================

    以下方法 将Excel模板文件存成 xml 格式的文本文件,从中把变化部分写成特殊的标志,然后根据实际的数据替换即可。 保存html格式也可.

    转自网络上
    http://www.16aspx.com/Article/3793
    无插件,无com组件,利用EXCEL、WORD模板做数据导出
      本次随笔主要讲述着工作中是如何解决数据导出的,对于数据导出到excel在日常工作中大家还是比较常用的,那导出到word呢,改如何处理呢,简单的页面导出问题应该不大,但是如果是标准的公文导出呢,要保证其基本格式,如红头、抬头、文号等等格式的限制我们又该如何处理呢?

    主要原理:

    1、利用excel、word做好模板,在模板中设置关键字

    2、在程序中调用模板,替换关键字

    3、将替换后的模板作为导出文件输出

    一、导出到EXCEL,在此处先从简单的入手,先描述如何利用excel做导出。步骤如下:

    1、用excel2007随便创建一个excel文件,打开添加如下数据:

    c#操作Excel模板,替换命名单元格或关键字形成报表

    2、点击另存,将excel文件另存为xml格式文件,选择2003xml格式,如下图:

    c#操作Excel模板,替换命名单元格或关键字形成报表

    3、右键用记事本打开刚刚另存到xml文件,观察xml内容会发现其妙之处,如下图:

    c#操作Excel模板,替换命名单元格或关键字形成报表

      如上图所示,注意观察,每一个WorkSheet都用了一段xml代码来表示,方框部分为我们写入的数据,圆圈部分为我们要处理的重要部分。

      小圆圈内的2表示当前数据2行,大圆圈表示我们看到的三行三列数据,因此我们要做的就是将我们要导出到数据写成圆圈中的格式就行了。

    4、改进xml文件,创建关键字,如下图:


    c#操作Excel模板,替换命名单元格或关键字形成报表

      如上图所示,我们创建了两个关键字,具体含义不多说。

      最后将创建好的xml文件,也就是你的excel模板可以放入到项目中了,接下来要做的就是将数据生成并按照标准格式输出,进行替换关键字即可。

    5、数据生成并输出,如下所示:

    public byte[] GetData(string path, string tempName)
            {
                //实例化内存流
                MemoryStream fileStream = new MemoryStream();
                //写文件流
                StreamWriter fileWriter = new StreamWriter(fileStream);
                //读文件流
                StreamReader fileReader = new StreamReader(path, System.Text.Encoding.UTF8);
                //读取整个文件的内容
                string tempStr = fileReader.ReadToEnd();
                //得到要导出的信息
                DataTable dt = airport.GetDataView();
                //定义变量获取表的数据行数
                int rows = 0;
                //定义StringBuilder变量来存储字符串
                StringBuilder sb = new StringBuilder();
                //判断表是否有数据
                if (dt != null && dt.Rows.Count > 0)
                {
                    //给变量赋值
                    rows = dt.Rows.Count;
                    int num = rows + 1;//此处1表示模板中表头的行数,且默认为1,可自行增加表头并定义
                    if (tempStr.IndexOf("+#RowCount#+") > 0)
                    {
                        tempStr = tempStr.Replace("+#RowCount#+", "" + num + "");
                    }
                    //开始循环生成标准字符串
                    for (int i = 0; i < rows; i++)
                    {
                        sb.Append(" "
                            + "" + dt.Rows[i]["Name"] + " "
                            + "" + dt.Rows[i]["Sex"] + " "
                            + "" + dt.Rows[i]["Age"] + " "
                            + " ");
                    }
                    if (tempStr.IndexOf("+�ta%+") > 0)
                    {
                        tempStr = tempStr.Replace("+�ta%+", sb.ToString());
                    }
                }
                fileWriter.WriteLine(tempStr);
                fileWriter.Flush();

                byte[] bytes = fileStream.ToArray();

                fileStream.Close();
                fileWriter.Close();
                fileReader.Close();

                return bytes;
            }如上所示,创建获取数据,并返回我们要输出的字符串,并转成二进制流,具体操作因人而异。接下来调用此方法替换数据,并输出excel新文件。

    protected void btnExport_Click(object sender, EventArgs e)
            {
                byte[] bytes = GetData(Server.MapPath("xml模板的路径"), "");

                //实现下载
                Response.AddHeader("Content-Type", "application/octet-stream");
                Response.Buffer = true;
                Response.ContentType = "*/*";
                Response.AddHeader("Content-Disposition", "attachment;filename=" + System.Web.HttpUtility.UrlEncode("导出的文件.xls", System.Text.Encoding.UTF8));
                Response.AddHeader("Content-Length", bytes.Length.ToString());
                Response.BinaryWrite(bytes);
                Response.End();

            }总上所述,一个完成的数据导出到EXCEL就完成了。
    优点:

    1、可以导出任何你可以在excel中能模拟出来的数据样式,包括图形嵌入。在后一篇会讲解如何导出图片到excel,word。

    2、整个过程不复杂,不需要调用插件、office组件,BS或者CS模式的方式都适用,没有复杂的逻辑过程。

    3、解决多sheet导出数据。

    缺点:需要先做模板

    注意事项:模板中的关键字要根据数据内容而定,不要出现关键字冲突的情况,关键字字符标准由开发者自行定夺。

  • 相关阅读:
    IIS7中的几种身份鉴别方式(一)Basic身份验证
    IIS7中的几种身份鉴别方式(二)集成身份验证
    java集合
    SharePoint 2010中welcome page的设置细节
    SharePoint中使用Linq出现未将对象引用到实例化的解决方法
    SharePoint 2010中关于An error was encountered while retrieving the user profile的处理方式记录
    The Need for an Architectural Body of Knowledge
    The Softer Side of the Architect
    Event Receivers 学习小结
    使用SmtpClient发送带图片的邮件的代码实现
  • 原文地址:https://www.cnblogs.com/guyandianzi/p/10469865.html
Copyright © 2011-2022 走看看