伊莉討論區
標題:
分享一些程式寫作的小技巧
[打印本頁]
作者:
rr09192084
時間:
2015-9-11 10:56 AM
標題:
分享一些程式寫作的小技巧
這是一個將 DataGridView 相關的一些常用到的功能,作一些小小分享,其中就包括轉成EXCEL檔案、列印等。
其中也有一些平常設計程式會用到的小技巧,諸如資料模組的設計,擴充法、資料型別的轉換等等,給大家參考。
這是Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Printing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Excel = Microsoft.Office.Interop.Excel;
namespace ListToDataGridView
{
public partial class Form1 : Form
{
//通常定義欄位都是以private為主
private List<PersonModel> person;
private string toExcelString = "";
private Timer timer;
//定義常數
private const int WM_CLOSE = 0x10;
public Form1()
{
InitializeComponent();
}
//API
[DllImport("User32.dll", EntryPoint = "FindWindow", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("User32.dll", EntryPoint = "PostMessage", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool PostMessage(IntPtr hWnd, uint Msg, UIntPtr wParam, IntPtr lParam);
private void Form1_Load(object sender, EventArgs e)
{
//建構時一次加入3筆資料
person = new List<PersonModel>()
{
new PersonModel
{
Name = "王大維",
ID = "B122111111",
Birthday = "19680705",
TEL = "07-23593512",
CellPhone = "0982-333-333",
Address = "高雄市苓雅區中華西路二段451號" ,
EMail = "daviwang@msa.hinet.net"
},
new PersonModel
{
Name = "姜中華",
ID = "C122221221",
Birthday = "19771025",
TEL = "04-23257066",
CellPhone = "0952-666-666",
Address = "台中市北屯區中西三路1026號",
EMail = "hwa661025888@yahoo.com.tw"
},
new PersonModel
{
Name = "張小明",
ID = "A122331331",
Birthday = "19780630",
TEL = "02-25805632",
CellPhone = "0988-999-999",
Address = "台北市大安區復興南路一段203號10樓之16",
EMail = "ming53621107@gmail.com"
}
};
//或是後來再加入1筆資料
person.Add(new PersonModel
{
Name = "李維勳",
ID = "T122551551",
Birthday = "19750630",
TEL = "02-23231122",
CellPhone = "0938-777-777",
Address = "台北市松山區信義路二段300號11樓之1",
EMail = "waishin@dodogogo.com"
});
//實際應用時也許會像這樣
/*
person.Add(new PersonModel
{
Name = textBox1.Text,
ID = textBox2.Text,
Birthday = textBox3.Text,
TEL = textBox4.Text,
CellPhone = textBox5.Text,
Address = textBox6.Text,
EMail = textBox7.Text
}); */
//=============================================================
//這段程式碼是將 List 放到 dataGridView1 裡
//這裡我在 ExtensionUtility.cs 這個類別裡寫的一個擴充方法
dataGridView1.DataSource = person.ToBindingSource<PersonModel>();
//=============================================================
//=============================================================
//設定欄位寬度,這段也可以放到dataGridView1_DataBindingComplete裡面
for (int i = 0; i < dataGridView1.ColumnCount; i++)
{
dataGridView1.Columns[i].AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells;
}
//=============================================================
}
//匯出EXCEL檔案功能按鈕
private void toExcelButton_Click(object sender, EventArgs e)
{
if (toExcelString == "") return;
//為了避免相互干擾,會強制關閉執行中EXCEL
initailExcel();
//所以要確認其它開啟的EXCEL檔案已經存檔並關閉。
Excel.Application xlApplication = new Excel.Application();
Excel.Workbook workBook = null;
Excel.Worksheet first_sheet = null;
Excel.Range tmprng = null;
string xlfilename = "";
FolderBrowserDialog fd = new FolderBrowserDialog();
if (fd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
if (fd.SelectedPath.Substring(fd.SelectedPath.Length-1, 1) == @"\")
//如果是選根目錄
xlfilename = fd.SelectedPath + @"(" + convertDate(DateTime.Now) + ")-個人資料(明細表).xlsx";
else
//選子目錄
xlfilename = fd.SelectedPath + @"\(" + convertDate(DateTime.Now) + ")-個人資料(明細表).xlsx";
}
else
{
return;
}
try
{
//EXCEL檔案如果已經存在就直接刪除
if (File.Exists(xlfilename)) { File.Delete(xlfilename); }
//建立新的EXCEL檔案
workBook = xlApplication.Workbooks.Add();
first_sheet = workBook.Sheets[1] as Excel.Worksheet;
//將文字複製到剪貼簿
Clipboard.SetText(toExcelString, TextDataFormat.UnicodeText);
//將C到E欄和G欄設定為文字格式並靠右對齊
tmprng = first_sheet.Range["C:E"];
tmprng.NumberFormat = "@";
tmprng.HorizontalAlignment = Microsoft.Office.Interop.Excel.XlHAlign.xlHAlignRight;
//在A1貼上剪貼簿上的內容
first_sheet.Paste(first_sheet.Range["A1", Type.Missing], false);
first_sheet.Name = "服務事項明細表";
//設定自動欄寬
first_sheet.Cells.EntireColumn.AutoFit();
//設定自動篩選
tmprng = first_sheet.get_Range("A1", "A1");
tmprng.AutoFilter(1, Type.Missing, Excel.XlAutoFilterOperator.xlAnd, Type.Missing, true);
//不詢問直接覆蓋原有檔案
xlApplication.DisplayAlerts = false;
//存檔為 EXCEL 2007 格式
workBook.SaveAs(xlfilename, Excel.XlFileFormat.xlWorkbookDefault);
}
catch (Exception ex)
{
//這一段是秀出錯誤訊息並且5秒鐘後自動關閉
StartTimerKillMessageBox(5);
MessageBox.Show(ex.Message + ex.ToString(), "MessageBox");
}
finally
{
//釋放掉一些資源的程式碼
NAR(tmprng);
NAR(first_sheet);
NAR(workBook);
xlApplication.Quit();
NAR(xlApplication);
GC.Collect();
if (File.Exists(xlfilename)) MessageBox.Show("匯出成功!!");
}
}
//列印功能按鈕
private void toPrinter_Click(object sender, EventArgs e)
{
if (toExcelString == "") return;
string tmpstr = "";
string[] ss = System.Text.RegularExpressions.Regex.Split(toExcelString, "\r\n");
foreach (string s in ss)
{ //以下這段程式是將文字稍作排版
if (s.Length < 5) continue;
string[] sv = System.Text.RegularExpressions.Regex.Split(s, "\t");
if (sv.Length == 7)
{
tmpstr += sv[0] + new String(' ', 12 - ChtStr_Lenght(sv[0]));
tmpstr += sv[1] + new String(' ', 11 - ChtStr_Lenght(sv[1]));
tmpstr += sv[2] + new String(' ', 11 - ChtStr_Lenght(sv[2]));
tmpstr += sv[3] + new String(' ', 16 - ChtStr_Lenght(sv[3]));
tmpstr += sv[4] + new String(' ', 16 - ChtStr_Lenght(sv[4]));
tmpstr += sv[5] + new String(' ', 40 - ChtStr_Lenght(sv[5]));
tmpstr += sv[6] + new String(' ', 28 - ChtStr_Lenght(sv[6])) + "\r\n";
}
}
PrintDocument p = new PrintDocument();
//把版面設定成橫印
p.DefaultPageSettings.Landscape = true;
p.PrintPage += delegate(object sender1, PrintPageEventArgs e1)
{ //這個列印程式只能印出第一頁,如果要印出多頁,這段程式要做修改
e1.Graphics.DrawString(tmpstr, new Font("標楷體", 11),
new SolidBrush(Color.Black),
new RectangleF(25, 50, p.DefaultPageSettings.PrintableArea.Height,
p.DefaultPageSettings.PrintableArea.Width));
};
try
{
p.Print();
}
catch (Exception ex)
{
throw new Exception("列印出錯:", ex);
}
}
private void dataGridView1_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
//每次DataBinding完成時都將person這個List轉成PersonExcelModel這個模組型態(LINQ語法)
//並且輸出成以TAB相隔的字串,方便貼到EXCEL工作表上
//其實這一整段基本上是可以放在Button_Click裡面的
toExcelString = "姓名\t證號\t生日\t電話\t手機\t地址\t電郵\r\n";
//這個是LINQ語法,我個人覺得是很好用東東
//=============================================================
var personexcel = from p in person
group p by new {
p.Name,
p.ID,
p.Birthday,
p.TEL,
p.CellPhone,
p.Address,
p.EMail
} into rs
select new PersonExcelModel()
{
姓名 = rs.Key.Name,
證號 = rs.Key.ID,
生日 = rs.Key.Birthday,
電話 = rs.Key.TEL,
手機 = rs.Key.CellPhone,
地址 = rs.Key.Address,
電郵 = rs.Key.EMail
};
//=============================================================
//所以personexcel現在已經是PersonExcelModel這種形態了
foreach (var item in personexcel)
{
toExcelString += item.ToString(); //這裡應用的就是之前複寫的ToString()方法
}
}
//啟動關閉訊息框
public void StartTimerKillMessageBox(int sec, string msg = "MessageBox")
{
timer = new Timer();
timer.Interval = sec * 1000; //等sec秒
timer.Tag = msg;
timer.Tick += new EventHandler(Timer_CloseMessageBox);
timer.Start();
}
public void Timer_CloseMessageBox(object sender, EventArgs e)
{
KillMessageBox(Convert.ToString(((Timer)sender).Tag));
//停止Timer
((Timer)sender).Stop();
}
public void KillMessageBox(string msg)
{
//依MessageBox的標題,找出MessageBox的視窗
IntPtr ptr = FindWindow(null, msg);
if (ptr != IntPtr.Zero)
{
//找到則關閉MessageBox視窗
PostMessage(ptr, WM_CLOSE, UIntPtr.Zero, IntPtr.Zero);
}
}
//傳回自訂格式的日期
public string convertDate(DateTime dt)
{
return dt.Year.ToString("0000") + dt.Month.ToString("00") + dt.Day.ToString("00");
}
//傳回有中文的字串的真實長度
public int ChtStr_Lenght(string a_SrcStr)
{
byte[] l_byte = System.Text.Encoding.Default.GetBytes(a_SrcStr);
return l_byte.Length;
}
//釋放占用的資源
public void NAR(object sender)
{
try
{
if (sender != null)
{
while (Marshal.ReleaseComObject(sender) > 0) ;
}
}
finally
{ sender = null; }
}
public void initailExcel()
{
//檢查PC有無Excel在執行,有的話強制關閉。
foreach (var item in System.Diagnostics.Process.GetProcesses())
{
if (item.ProcessName.ToUpper() == "EXCEL")
{
item.Kill();
item.WaitForExit();
}
}
}
}
}
複製代碼
這是DataModel.cs,用來設計資料的模組
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ListToDataGridView
{
class PersonModel : IEquatable<PersonModel>
{
public string Name { get; set; }
public string ID { get; set; }
public string Birthday { get; set; }
public string TEL { get; set; }
public string CellPhone { get; set; }
public string Address { get; set; }
public string EMail { get; set; }
//IEquatable這個介面需要實作Equals這個方法
public bool Equals(PersonModel other)
{
return this.ID.Equals(other.ID);
}
}
class PersonExcelModel : IEquatable<PersonExcelModel>
{
public string 姓名 { get; set; }
public string 證號 { get; set; }
public string 生日 { get; set; }
public string 電話 { get; set; }
public string 手機 { get; set; }
public string 地址 { get; set; }
public string 電郵 { get; set; }
//IEquatable這個介面需要實作Equals這個方法
public bool Equals(PersonExcelModel other)
{
return this.證號.Equals(other.證號);
}
//再寫一個多載方法可以和PersonModel做比對
public bool Equals(PersonModel other)
{
return this.證號.Equals(other.ID);
}
//這裡我們複寫ToString()這個方法,以便讓它可以符合輸出到EXCEL的需求
public override string ToString()
{
return this.姓名 + "\t" + this.證號 + "\t" + this.生日
+ "\t" + this.電話 + "\t" + this.手機
+ "\t" + this.地址 + "\t" + this.電郵 + "\r\n";
}
}
}
複製代碼
這是ExtensionUtility.cs,裡面裝了一個把List轉成BindingSource的擴充方法
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ListToDataGridView
{
public static class ExtensionUtility
{
//擴充方法,有興趣自己找資料研究,可以先看看MSDN的說明
//https://msdn.microsoft.com/zh-tw/library/bb383977.aspx
public static BindingSource ToBindingSource<TSource>(this IList<TSource> data)
{
var bindingList = new BindingList<TSource>(data);
return new BindingSource(bindingList, null);
}
}
}
複製代碼
請大家不吝指教
作者:
rr09192084
時間:
2015-9-13 01:59 PM
列印與匯出成EXCEL檔案部分有做一些修正與加強,列印部分改成可以列出多頁。匯出EXCEL部分增加了會出後直接開啟EXCEL檔案的功能。
要改的地方蠻多的
1.在欄位的地方增加
private PrintDocument pd = new PrintDocument();
private string stringToPrint = "";
複製代碼
2.在Form_Load裏加上
pd.PrintPage += new PrintPageEventHandler(printDocument_PrintPage);
複製代碼
3.把 或是後來再加入1筆資料 這一段改成下面這一段程式,直接增加70筆資料,列印時會分成2頁。
//或是後來再加入70筆資料
for (int i = 0; i < 70; i++)
{
int idnum = 122551551;
person.Add(new PersonModel
{
Name = "李維勳",
ID = "T" + (idnum + i).ToString(), //ID不同才會被視為不同一筆資料
Birthday = "19750630",
TEL = "02-23231122",
CellPhone = "0938-777-777",
Address = "台北市松山區信義路二段300號11樓之1",
EMail = "waishin64@extenalmail.dodogogo.com.tw"
});
}
複製代碼
4.在toExcelButton_Click裏面finally這一段改成
//釋放掉一些資源的程式碼
NAR(tmprng);
NAR(first_sheet);
NAR(workBook);
xlApplication.Quit();
NAR(xlApplication);
GC.Collect();
if (File.Exists(xlfilename))
{
StartTimerKillMessageBox(3);
MessageBox.Show("匯出成功!!3秒後將會開啟檔案...", "MessageBox");
System.Diagnostics.Process ps = new System.Diagnostics.Process();
//開啟EXCEL檔案
ps.StartInfo = new System.Diagnostics.ProcessStartInfo(xlfilename);
//或是開啟EXCEL檔案所在的目錄
//ps.StartInfo = new System.Diagnostics.ProcessStartInfo(fd.SelectedPath);
ps.Start();
}
複製代碼
5.把toPrinter_Click裡面所有程式碼改成
if (toExcelString == "") return;
string tmpstr = "";
string[] ss = System.Text.RegularExpressions.Regex.Split(toExcelString, "\r\n");
foreach (string s in ss)
{ //以下這段程式是將文字稍作排版
if (s.Length < 5) continue;
string[] sv = System.Text.RegularExpressions.Regex.Split(s, "\t");
if (sv.Length == 7)
{
//姓名是中文資料,要另外處理,所以又加了另一個擴充方法ToChtSubstring
tmpstr += (ChtStr_Length(sv[0]) <= 12) ? sv[0] + new String(' ', 12 - ChtStr_Length(sv[0])) : sv[0].ToChtSubstring(0, 12);
tmpstr += (sv[1].Length <= 11) ? sv[1] + new String(' ', 11 - ChtStr_Length(sv[1])) : sv[1].Substring(0, 11);
tmpstr += (sv[2].Length <= 11) ? sv[2] + new String(' ', 11 - ChtStr_Length(sv[2])) : sv[2].Substring(0, 11);
tmpstr += (sv[3].Length <= 16) ? sv[3] + new String(' ', 16 - ChtStr_Length(sv[3])) : sv[3].Substring(0, 16);
tmpstr += (sv[4].Length <= 16) ? sv[4] + new String(' ', 16 - ChtStr_Length(sv[4])) : sv[4].Substring(0, 16);
//地址也是中文資料,同樣另外處理
tmpstr += (ChtStr_Length(sv[5]) <= 42) ? sv[5] + new String(' ', 42 - ChtStr_Length(sv[5])) : sv[5].ToChtSubstring(0, 42);
tmpstr += (sv[6].Length <= 40) ? sv[6] + new String(' ', 40 - ChtStr_Length(sv[6])) : sv[6].Substring(0, 40);
tmpstr += "\r\n";
}
}
stringToPrint = tmpstr;
try
{
//設定成橫印
pd.DefaultPageSettings.Landscape = true;
//列印輸出
//叫出列印對話框
PrintDialog pdialog = new PrintDialog();
pdialog.Document = pd;
if (pdialog.ShowDialog() == DialogResult.OK)
{
pd.Print();
}
}
catch (Exception ex)
{
throw new Exception("列印出錯:", ex);
}
複製代碼
6.增加一個PrintDocument處理PrintPage事件的函式
private void printDocument_PrintPage(object sender, PrintPageEventArgs e)
{
//每頁字數
int charactersOnPage = 0;
//每頁的列數
int linesPerPage = 0;
//目前預設印表機可印範圍的高度
float h = pd.DefaultPageSettings.PrintableArea.Height - 40;
//目前預設印表機可印範圍的寬度(這裡將兩個參數減掉一些數據,是因為通常都不會滿版列印)
float w = pd.DefaultPageSettings.PrintableArea.Width - 80;
//如果是設定為橫印時,要將寬與高對調
if (pd.DefaultPageSettings.Landscape)
{
float tmp = h;
h = w;
w = tmp;
}
//計算出每頁字數,每頁列數
e.Graphics.MeasureString(stringToPrint,
new Font("標楷體", 11),
new SizeF(w, h),
StringFormat.GenericTypographic,
out charactersOnPage, //輸出給每頁字數的變數
out linesPerPage); //輸出給每頁列數的變數
//畫出列印頁面
e.Graphics.DrawString(stringToPrint,
new Font("標楷體", 11),
Brushes.Black,
new RectangleF(20, 50, w, h), //這是跟邊界有關的4個參數
StringFormat.GenericTypographic);
//取出每一頁字數的剩餘部分
stringToPrint = stringToPrint.Substring(charactersOnPage);
//一直到沒有頁面可以列印為止
e.HasMorePages = (stringToPrint.Length > 0);
}
複製代碼
7.在ExtensionUtility裏增加一個ToChtSubstring()的擴充方法
public static String ToChtSubstring(this String s, int starindex, int lenght)
{
byte[] linestr = System.Text.Encoding.Default.GetBytes(s);
return System.Text.Encoding.Default.GetString(linestr, starindex, lenght);
}
複製代碼
嫌麻煩的話可以直接下載整個專案檔案來參考看看喔
http://1drv.ms/1NwKu9w
作者:
smallanan
時間:
2015-9-24 12:04 AM
提示:
作者被禁止或刪除 內容自動屏蔽
作者:
rr09192084
時間:
2015-9-24 12:42 AM
smallanan 發表於 2015-9-24 12:04 AM
個人會比較建議如果依照datagridview一模一樣的方式匯出excel檔案的話
使用odbc效率會比較好(而且不用安 ...
哈哈,我說怎麼都沒人回應呢?感謝您的指教!!
我本來後續要貼一些EXCEL排版相關的功能,還有套表列印信封、賀卡等等...
跟資料庫的連接、利用LINQ做一些樞紐分析等等...
看到沒人回應,想就樣做罷了說
作者:
孤單小呆呆
時間:
2015-10-1 01:22 PM
最近剛好從C++跳過來
很多都還看不懂
感謝大大的分享
讓我有學習的地方
作者:
superwaterdog
時間:
2015-11-19 09:00 PM
還不錯的介紹
程式的基礎架構都有
當然純看CODE 會有一些基礎會比較好
如有用到INTERFACE 等技巧
作者:
Enjoyeyny51
時間:
2015-12-25 12:29 AM
提示:
作者被禁止或刪除 內容自動屏蔽
作者:
superwaterdog
時間:
2016-2-24 10:35 PM
不錯的範例
謝謝分享
不過要有一點工夫才看得懂
作者:
licious0421
時間:
2016-2-26 04:13 PM
很棒呀!!!
我也算是C#新手八= =
剛好目前有遇到~DATAGREADVIEW TO EXCEL~
完全就是我的需要阿!!
作者:
c15722145
時間:
2016-3-4 08:21 AM
不建議這樣操作Excel
樓上ODBC方式較佳
不然也可找open source有可以直接生成excel檔案的方法
作者:
bigpig200126
時間:
2016-3-13 09:08 PM
小弟也是新手,最近在研究Kendo UI ,發現他蠻好用的,可以直接幫你把Girid的資料產出成Excel,完全不用寫程式,你可以試試看唷
作者:
min721107
時間:
2020-4-15 02:18 PM
謝謝大大分享,收下使用
作者:
jasonme168
時間:
2020-8-27 10:55 PM
神人!
感謝您的分享,造福後學。
作者:
SIHEOT
時間:
2021-1-2 03:59 AM
太神啦
感謝大大的分享
歡迎光臨 伊莉討論區 (http://a04.eyny.com/)
Powered by Discuz!