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導出數據。
缺點:需要先做模板
注意事項:模板中的關鍵字要根據數據內容而定,不要出現關鍵字沖突的情況,關鍵字字符標准由開發者自行定奪。