|
如果你耐心仔細(xì)看完本文,相信以后再遇到導(dǎo)出EXCLE操作的時候你會很順手覺得SO EASY,主要給新手朋友們看的,老鳥可以直接飄過了,花了一晚上的時間寫的很辛苦,如果覺得對你有幫助煩請留言支持一下,我會寫更多基礎(chǔ)的原創(chuàng)內(nèi)容來回報大家。
C#導(dǎo)出數(shù)據(jù)到EXCEL表格是個老生常談的問題了,寫這篇文章主要是給和我一樣的新手朋友提供兩種導(dǎo)出EXCEL的方法并探討一下導(dǎo)出的效率問題,本文中的代碼直接就可用,其中部分代碼參考其他的代碼并做了修改,拋磚引玉,希望大家一起探討,如有不對的地方還請大家多多包涵并指出來,我也是個新手,出錯也是難免的。
首先先總結(jié)下自己知道的導(dǎo)出EXCEL表格的方法,大致有以下幾種,有疏漏的請大家補(bǔ)充。
1.數(shù)據(jù)逐條逐條的寫入EXCEL
2.通過OLEDB把EXCEL做為數(shù)據(jù)源來寫
3.通過RANGE范圍寫入多行多列內(nèi)存數(shù)據(jù)到EXCEL
4.利用系統(tǒng)剪貼板寫入EXCEL
好了,我想這些方法已經(jīng)足夠完成我們要實現(xiàn)的功能了,方法不在多,在精,不是么?以上4中方法都可以實現(xiàn)導(dǎo)出EXCEL,方法1為最基礎(chǔ)的方法,意思就是效率可能不是太高,當(dāng)遇到數(shù)據(jù)量過大時所要付出的時間也是巨大的,后面3種方法都是第一種的衍生,在第一種方法效率低下的基礎(chǔ)上改進(jìn)的,這里主要就是一個效率問題了,當(dāng)然如果你數(shù)據(jù)量都很小,我想4種方法就代碼量和復(fù)雜程度來說第1種基本方法就可以了,或當(dāng)你的硬件非常牛逼了,那再差的方法也可以高效的完成也沒有探討的實際意義了,呵呵說遠(yuǎn)了,本文主要是在不考慮硬件或同等硬件條件下單從軟件角度出發(fā)探討較好的解決方案。
此項目代碼已打包在附件中,幾乎注釋的無微不至,在配合本文中我唐僧般的娓娓道來(靠,誰丟臭雞蛋砸俺了),相信即使剛?cè)腴TC#的朋友也能看得懂了吧,大家可以自行下載,代碼中演示了兩種方法,上述的方法1和方法3,我想足夠了,方法3的效率應(yīng)該是四種中最高的了,其他兩種有興趣的朋友自己實現(xiàn)下哈(方法2在我的程序中也有用到一些,看完你就知道了),程序如下圖所示,先加載一個EXCEL表格進(jìn)DATAGRIDVIEW作為數(shù)據(jù)源,然后兩種方法導(dǎo)出EXCEL,配上一個計數(shù)功能給大家直觀的看到導(dǎo)出的耗時,大家可以準(zhǔn)備個幾萬行和十多行的兩個EXCEL作為數(shù)據(jù)源來測試,也可以用我打包里的XLS來測試下。
0.png (54.77 KB, 下載次數(shù): 77)
下載附件
2017-6-5 17:00 上傳
首先使用前都需要加載COM組件Microsoft.Office.Interop.Excel.dll(已打包)
關(guān)鍵代碼如下:
方法1,最基本方法,用FOR循環(huán)逐條寫入EXCEL的CELL中,其他的看代碼就行了,關(guān)鍵代碼如下
public void ToExcel1(DataGridViewgridView, SaveFileDialog saveFileDialog)
{
...........................
for (int i = 0; i < gridView.RowCount; i++)
{
for (int j = 0; j < gridView.ColumnCount; j++)
{
if (gridView[j,i].Value == typeof(string))
{
excel.Cells[i +2, j + 1] = "" + gridView[i,j].Value.ToString();
}
else
{
excel.Cells[i +2, j + 1] = gridView[j, i].Value.ToString();
}
}
//進(jìn)度條加1
progressBar1.Value++;
/*
* 注意此Application.DoEvents(),如果無此句,當(dāng)切換窗口后回到本程序無法重繪窗體會出現(xiàn)假死狀態(tài)
* 此處我試過用委托和線程異步調(diào)用的方法,但效果沒有這句效果好
*/ System.Windows.Forms.Application.DoEvents();
...................
}
方法2,快速保存內(nèi)存中大量數(shù)據(jù)到Excel的WorkSheet。關(guān)鍵之處是使用Range一次存儲多行多列數(shù)據(jù)。
public void ToExcel2(DataGridViewgridView, SaveFileDialog saveFileDialog)
{
....................
System.Reflection.Missing miss = System.Reflection.Missing.Value;
//創(chuàng)建EXCEL對象appExcel,Workbook對象,Worksheet對象,Range對象
Microsoft.Office.Interop.Excel.Application appExcel;
appExcel = new Microsoft.Office.Interop.Excel.Application();
Microsoft.Office.Interop.Excel.Workbook workbookData;
Microsoft.Office.Interop.Excel.Worksheet worksheetData;
Microsoft.Office.Interop.Excel.Range rangedata;
//設(shè)置對象不可見
appExcel.Visible= false;
/* 在調(diào)用Excel應(yīng)用程序,或創(chuàng)建Excel工作簿之前,記著加上下面的兩行代碼
* 這是因為Excel有一個Bug,如果你的操作系統(tǒng)的環(huán)境不是英文的,而Excel就會在執(zhí)行下面的代碼時,報異常。
*/
System.Globalization.CultureInfo CurrentCI = System.Threading.Thread.CurrentThread.CurrentCulture; System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US");
workbookData =appExcel.Workbooks.Add(miss);
worksheetData=(Microsoft.Office.Interop.Excel.Worksheet)workbookData.Worksheets.Add(miss,miss, miss, miss);
//給工作表賦名稱
worksheetData.Name= "saved";
//清零計數(shù)并開始計數(shù)
TimeP = new System.DateTime(0);
timer1.Start();
label1.Text =TimeP.ToString("HH:mm:ss");
// 保存到WorkSheet的表頭,你應(yīng)該看到,是一個Cell一個Cell的存儲,這樣效率特別低,解決的辦法是,使用Rang,一塊一塊地存儲到Excel
for (int i = 0; i < gridView.ColumnCount; i++)
{
worksheetData.Cells[1,i + 1] = gridView.Columns[ i].HeaderText.ToString();
}
//先給Range對象一個范圍為A2開始,Range對象可以給一個CELL的范圍,也可以給例如A1到H10這樣的范圍
//因為第一行已經(jīng)寫了表頭,所以所有數(shù)據(jù)都應(yīng)該從A2開始
rangedata =worksheetData.get_Range("A2",miss);
Microsoft.Office.Interop.Excel.Range xlRang = null;
//iRowCount為實際行數(shù),最大行
int iRowCount =gridView.RowCount;
int iParstedRow =0, iCurrSize = 0;
//iEachSize為每次寫行的數(shù)值,可以自己設(shè)置,每次寫1000行和每次寫2000行大家可以自己測試下效率
int iEachSize =1000;
//iColumnAccount為實際列數(shù),最大列數(shù)
int iColumnAccount= gridView.ColumnCount;
//在內(nèi)存中聲明一個iEachSize×iColumnAccount的數(shù)組,iEachSize是每次最大存儲的行數(shù),iColumnAccount就是存儲的實際列數(shù)
object[,] objVal = new object[iEachSize,iColumnAccount];
try
{
//給進(jìn)度條賦最大值為實際行數(shù)最大值
progressBar1.Maximum= gridView.RowCount;
iCurrSize =iEachSize;
while (iParstedRow< iRowCount)
{
if ((iRowCount -iParstedRow) < iEachSize)
iCurrSize =iRowCount - iParstedRow;
//用FOR循環(huán)給數(shù)組賦值
for (int i = 0; i < iCurrSize; i++)
{
for (int j = 0; j < iColumnAccount; j++)
objVal[i, j] =gridView[j, i + iParstedRow].Value.ToString();
progressBar1.Value++; System.Windows.Forms.Application.DoEvents();
}
/*
* 建議使用設(shè)置斷點(diǎn)研究下哈
* 例如A1到H10的意思是從A到H,第一行到第十行
* 下句很關(guān)鍵,要保證獲取Sheet中對應(yīng)的Range范圍
* 下句實際上是得到這樣的一個代碼語句xlRang= worksheetData.get_Range("A2","H100");
* 注意看實現(xiàn)的過程
*'A' + iColumnAccount - 1這兒是獲取你的最后列,A的數(shù)字碼為65,大家可以仔細(xì)看下是不是得到最后列的字母
*iParstedRow + iCurrSize + 1獲取最后行
* 若WHILE第一次循環(huán)的話這應(yīng)該是A2,最后列字母+最后行數(shù)字
*iParstedRow + 2要注意,每次循環(huán)這個值不一樣,他取決于你每次循環(huán)RANGE取了多大,循環(huán)了幾次,也就是iEachSize設(shè)置值的大小哦
*/
xlRang =worksheetData.get_Range("A" + ((int)(iParstedRow + 2)).ToString(), ((char)('A' +iColumnAccount - 1)).ToString() + ((int)(iParstedRow+ iCurrSize + 1)).ToString());
// 調(diào)用Range的Value2屬性,把內(nèi)存中的值賦給Excel
xlRang.Value2 =objVal;
iParstedRow =iParstedRow + iCurrSize;
}
//保存工作表
worksheetData.SaveAs(strName,miss, miss, miss, miss, miss, Microsoft.Office.Interop.Excel.XlSaveAsAccessMode.xlNoChange, miss, miss,miss); System.Runtime.InteropServices.Marshal.ReleaseComObject(xlRang);
xlRang = null;
progressBar1.Value= 0;
//調(diào)用方法關(guān)閉EXCEL進(jìn)程,大家可以試下不用的話如果程序不關(guān)閉在進(jìn)程里一直會有EXCEL.EXE這個進(jìn)程并鎖定你的EXCEL表格
this.KillSpecialExcel(appExcel);
timer1.Stop();
MessageBox.Show("數(shù)據(jù)已經(jīng)成功導(dǎo)出到:" +saveFileDialog.FileName.ToString(), "導(dǎo)出完成", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
timer1.Stop();
return;
}
// 別忘了在結(jié)束程序之前恢復(fù)你的環(huán)境! System.Threading.Thread.CurrentThread.CurrentCulture = CurrentCI;
最后再順便說下本程序中的一些部分:
1.做過EXCEL導(dǎo)出的朋友應(yīng)該遇到過一個情況,當(dāng)導(dǎo)出完畢后進(jìn)程中那個討厭的EXCEL.EXE老是無法關(guān)閉,造成鎖定導(dǎo)出的XLS文件,要關(guān)閉程序后該進(jìn)程才退出,網(wǎng)上也有一種方法就是我方法ToExcel1()用到的那個方法(詳見下載的代碼),但我寫ToExcel2()方法的時候發(fā)現(xiàn)這種關(guān)閉的方法突然不好使了,這我用到了另一種方法KillSpecialExcel,調(diào)用的時候這樣調(diào)用this.KillSpecialExcel(appExcel)大家可以試下把ToExcel2()改為ToExcel1()的關(guān)閉EXCEL方法看是否有效,代碼如下
#region 結(jié)束EXCEL.EXE進(jìn)程的方法
/// <summary>
/// 結(jié)束EXCEL.EXE進(jìn)程的方法
/// </summary>
/// <paramname="m_objExcel">EXCEL對象</param>
[DllImport("user32.dll",SetLastError = true)]
static extern intGetWindowThreadProcessId(IntPtr hWnd, out intlpdwProcessId);
public void KillSpecialExcel(Microsoft.Office.Interop.Excel.Application m_objExcel)
{
try
{
if (m_objExcel !=null)
{
int lpdwProcessId;
GetWindowThreadProcessId(new IntPtr(m_objExcel.Hwnd),out lpdwProcessId); System.Diagnostics.Process.GetProcessById(lpdwProcessId).Kill();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
2.代碼里寫了個加載EXCEL到DATAGRIDVIEW的方法,就是想本來就是在寫EXCEL操作相關(guān)嘛,干脆數(shù)據(jù)源也用EXCEL,該方法就是把EXCEL作為OLEDB數(shù)據(jù)源這樣來操作的,你是不是想到前面我所提到的四種導(dǎo)出EXCEL中的《方法2通過OLEDB把EXCEL做為數(shù)據(jù)源來寫》呢,對了,有興趣你可以嘗試修改下通過OLEDB這樣來導(dǎo)出EXCEL,當(dāng)然,新手朋友可以看下如何加載EXCEL數(shù)據(jù),你應(yīng)該希望去嘗試接著如何把這些數(shù)據(jù)儲存到數(shù)據(jù)庫之類的操作
3.System.Windows.Forms.Application.DoEvents(),當(dāng)運(yùn)行 Windows 窗體時,它將創(chuàng)建新窗體,然后該窗體等待處理事件。該窗體在每次處理事件時,均將處理與該事件關(guān)聯(lián)的所有代碼。所有其他事件在隊列中等待。在代碼處理事件時,應(yīng)用程序并不響應(yīng)。例如,當(dāng)將另一窗口拖到該窗口前面時,該窗口不重新繪制。如果在代碼中調(diào)用 DoEvents,則您的應(yīng)用程序可以處理其他事件。例如,如果您有向 ListBox 添加數(shù)據(jù)的窗體,并將 DoEvents 添加到代碼中,那么當(dāng)將另一窗口拖到您的窗體上時,該窗體將重新繪制。如果從代碼中移除 DoEvents,那么在按鈕的單擊事件處理程序執(zhí)行結(jié)束以前,您的窗體不會重新繪制。
這里說遠(yuǎn)一點(diǎn),關(guān)于如何防止UI假死其實還有更科學(xué)的方法,就是使用委托加線程異步執(zhí)行,把耗時的操作另起進(jìn)程執(zhí)行,從而減輕UI的壓力,使得前臺UI流暢運(yùn)行而不假死,多用this.Invoke等,但在我程序用我也測試過反而直接DoEvents效果還好的多,而且線程和委托可能對新手朋友稍微復(fù)雜了些,推薦一般應(yīng)用用DoEvents足夠了哦。
好了,東西不多廢話一大篇,手都寫累了,基本上用到的方法都做了詳細(xì)的解釋了,呵呵,希望以后大家多多交流,本人菜鳥一個,寫本文就是因為自己也得到過很多熱心網(wǎng)友的指導(dǎo)和幫助,也希望分享一些自己的東西作為回報,人人為我,我為人人,學(xué)會做個懂得感恩的人比什么都重要,歡迎大家和我一起學(xué)習(xí)套談C#
0.png (40.63 KB, 下載次數(shù): 96)
下載附件
2017-6-5 16:51 上傳
全部資料下載地址:
C#導(dǎo)出數(shù)據(jù)到EXCEL方法談(附實例源碼和超級無敵詳細(xì)講解).rar
(560.85 KB, 下載次數(shù): 74)
2017-6-5 16:22 上傳
點(diǎn)擊文件名下載附件
下載積分: 黑幣 -5
|
評分
-
查看全部評分
|