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导出数据。

    缺点:需要先做模板

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

  • 相关阅读:
    jQuery 操作元素属性、内容、css、class
    jQuery 选择器
    JavaScript 二级菜单(含过渡动画)
    tools.js
    JavaScript JSON
    中华人民共和国国务院令第644号 国务院关于修改《全国年节及纪念日放假办法》的决定
    Linux系统Yum仓库制作
    玩转Linux系统之常用命令
    IDEA安装插件提示was not installed: Cannot download解决办法
    软件开发过程中常用的环境解释DEV FAT UAT PRO
  • 原文地址:https://www.cnblogs.com/guyandianzi/p/10469865.html
Copyright © 2011-2022 走看看