c#操作Excel模板,替换命名单元格或关键字形成报表
c#操作Excel模板,替换命名单元格或关键字形成报表
http://blog.sina.com.cn/s/blog_45eaa01a0102vqma.html
一 建立Excel 模板文件 template.xls
1.1 插入命名单元格的方法:
左上角名称框,显示当前单元格的行列号C2,加入命名后会显示其命名name
方法一:
(1) 点击 单元格“姓名”之后的单元格
(2) 菜单 插入--名称--定义
(3) 在框中输入 name
确保底部的引用位置为 =Users!$C$2
按“添加”、“确定”按钮 即可
方法二:
(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列,插入名称 _结论
二 建立一个,WindowForm格式的解决方案WindowsFormsApplication10
三 添加对Excel的引用
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;
3 注意:如果工程中,曾经加入过Excel,office,VBIDE相同的引用,则再加入后,
需要从该引用的“属性”中,将“潜入互操作类型”从True 改变为 False,否则无法编译
四 建立一个操作EXCEL的类ExcelTemplate,并在实际工程中,创建类对象即可。
特别注意:为了程序自动处理方便起见,命名单元格的规则如下:
1.1 开头处的命名单元格,以1个下划线开始,比如,_报告日期
1.2 中间多行组循环的命名单元格,不加下划线,且与数据集的字段名一致为好
1.3 结尾处的命名单元格,以2个下划线开头,比如,__合计
五 结果如下:
六 完整代码如下:
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:\WindowsFormsApplication10\WindowsFormsApplication10\bin\Debug\
//d:\WindowsFormsApplication10\WindowsFormsApplication10\template.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, "\t", 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文件,打开添加如下数据:
2、点击另存,将excel文件另存为xml格式文件,选择2003xml格式,如下图:
3、右键用记事本打开刚刚另存到xml文件,观察xml内容会发现其妙之处,如下图:
如上图所示,注意观察,每一个WorkSheet都用了一段xml代码来表示,方框部分为我们写入的数据,圆圈部分为我们要处理的重要部分。
小圆圈内的2表示当前数据2行,大圆圈表示我们看到的三行三列数据,因此我们要做的就是将我们要导出到数据写成圆圈中的格式就行了。
4、改进xml文件,创建关键字,如下图:
如上图所示,我们创建了两个关键字,具体含义不多说。
最后将创建好的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("\n"
+ "" + dt.Rows[i]["Name"] + "\n"
+ "" + dt.Rows[i]["Sex"] + "\n"
+ "" + dt.Rows[i]["Age"] + "\n"
+ "\n");
}
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导出数据。
缺点:需要先做模板
注意事项:模板中的关键字要根据数据内容而定,不要出现关键字冲突的情况,关键字字符标准由开发者自行定夺。
c#操作Excel模板,替换命名单元格或关键字形成报表的更多相关文章
- JAVA操作Excel时文字自适应单元格的宽度设置方法
使用JAVA操作Excel通常都使用JXL,方法很简单网上也有很多的教程,然后往往一些细节性的问题却导致我们这些Programmer苦恼不已.这两天帮一个朋友做一个Excel表格自动生成的小软件,就遇 ...
- C# 如何使用NPOI操作Excel以及读取合并单元格等
C#操作Excel方法有很多,以前用的需要电脑安装office才能用,但因为版权问题公司不允许安装office.所以改用NPOI进行Excel操作,基本上一些简单的Excel操作都没有问题,读写合并单 ...
- 【转载】jxl操作excel 字体 背景色 合并单元格 列宽等 .
package com.email.jav; import java.io.File;import java.io.IOException;import java.net.URL; import jx ...
- java操作Excel的poi 设置单元格的对其方式
设置单元格的对其方式 package com.java.poi; import org.apache.poi.hssf.usermodel.HSSFCell; import org.apache.po ...
- Excel应该这么玩——1、命名单元格:干掉常数
命名单元格:通过名称来引用单元格中的值,常用于引用固定不变的值. 单元格是Excel中存储数据的最小单位,在公式中通过A1.B2之类的名称来引用其中的值.A1只是单元格的坐标,就好像人的身份证号.生活 ...
- 工作总结 1 sql写法 insert into select from 2 vs中 obj文件和bin文件 3 npoi 模板copy CopySheet 最好先全部Copy完后 再根据生成sheet写数据 4 sheet.CopyRow(rowsindex, rowsindex + x); 5 npoi 复制模板如果出现单元格显示问题
我们可以从一个表中复制所有的列插入到另一个已存在的表中: INSERT INTO table2SELECT * FROM table1; 或者我们可以只复制希望的列插入到另一个已存在的表中: INSE ...
- excel VBA把一个单元格内容按逗号拆分并依次替换到另一个单元格的括号里面(本题例子,把文本中的括号换成{答案}的格式,并按顺序填空)
方法1:运用excel单元格拆分合并实现 思路:用VBA正则查询左侧括号个数,对右侧单元格逐一按逗号.顿号等符号分列,同时左侧按括号分列(分列只能按括号单边分列),分列完成后按要求合并,本题事例把括号 ...
- C# 获取Excel中的合并单元格
C# 获取Excel中的合并单元格 我们在制作表格时,有时经常需要合并及取消合并一些单元格.在取消合并单元格时需要逐个查找及取消,比较麻烦.这里分享一个简单的方法来识别Excel中的合并单元格,识别这 ...
- 填报表导出excel非可写单元格锁定问题
问题描述: 填报表单元格分为可写和不可写两种状态,当填报表在web上展现的时候可写单元格可以进行数据填报和修改,非可写单元格不可操作. 报表导出为excel时,润乾导出excel包默认情况下不对 ...
随机推荐
- cpu_relax
https://blog.csdn.net/justlinux2010/article/details/8533451
- Windows平台安装TensorFlow Q&A
·本文讲的是Windows平台使用原生pip进行TensorFlow(CPU版本)安装的注意事项及常见问题解决方法 ·这是TensorFlow官网的安装介绍:在 Windows 上安装 TensorF ...
- hashChange & url change & QRCode & canvas to image
hashChange & url change & QRCode & canvas to image "use strict"; /** * * @auth ...
- element vue 表格编辑
https://xuliangzhan.github.io/vue-element-extends/#/editable/click1
- pixy&STM32使用记录(串口&SPI外设)
先踏踏实实的把stm32的外设串口,SPI搞清楚,不要眼高手低,看不起小事.用SPI通信将pixy的数据读出来,将数据用串口发到串口助手上,然后处理数据,利用STM32的定时器调节pwm,控制电机,先 ...
- JetBrain系列IDE提示Filesystem Case-Sensitivity Mismatch的解决
目录 解决方法 1. 用文本编辑器修改APP包文件中的属性文件(不推荐) 2. 复制或新建属性文件到APP的启动目录,添加对应的属性项(推荐) 解决方法1 1. 用文本编辑器修改APP包文件中的属性文 ...
- code runner 使用教程
https://zhuanlan.zhihu.com/p/54861567 其中解决无法在编辑器中编辑问题(编辑器只读) 只需要把Code-runner: Run In Terminal true(打 ...
- 【BZOJ3997】[TJOI2015]组合数学(动态规划)
[BZOJ3997][TJOI2015]组合数学(动态规划) 题面 BZOJ 洛谷 题解 相当妙的一道题目.不看题解我只会暴力网络流 先考虑要求的是一个什么东西,我们把每个点按照\(a[i][j]\) ...
- 「洛谷5300」「GXOI/GZOI2019」与或和【单调栈+二进制转化】
题目链接 [洛谷传送门] 题解 按位处理. 把每一位对应的图都处理出来 然后单调栈处理一下就好了. \(and\)操作处理全\(1\). \(or\)操作处理全\(0\). 代码 #include & ...
- django restframework Serializer field
SerializerMethodField 这是一个只读字段.它通过调用它所连接的序列化类的方法来获得它的值.它可用于将任何类型的数据添加到对象的序列化表示中. 签名: SerializerMetho ...