Winform里集成了幾個(gè)打印控件:PrintPreviewDialog,PrintDocument,PrintDialog,PageSetupDialog,PrintPreviewControl,看書看資料會(huì)把人看暈,實(shí)際上打印核心控件只有一個(gè),就是PrintDocument,PrintPreviewDialog也有點(diǎn)用,就是頁(yè)面預(yù)覽控件,真正開發(fā)中如果需要打印功能,一般會(huì)從網(wǎng)上Down現(xiàn)成的控件,如果要自己手動(dòng)寫,費(fèi)時(shí)又費(fèi)力,本著學(xué)習(xí)的態(tài)度,我們需要知道打印是怎么實(shí)現(xiàn)的,寫兩個(gè)比較常用的打印服務(wù),一個(gè)是打印DataTable的,另一個(gè)是打印TextBox的。 示例一:以表格形式打印DataTable內(nèi)的數(shù)據(jù)//定義全局變量count,儲(chǔ)存當(dāng)前打印的行數(shù)int count=0;//定義一個(gè)方法,接收一個(gè)DataTable類型參數(shù)及PrintDocument的PrintPage事件傳入的參數(shù)e以方便操作private voidPrintTable(DataTable dt, System.Drawing.Printing.PrintPageEventArgse){ //取得對(duì)應(yīng)的Graphics對(duì)象 Graphicsg = e.Graphics; //獲得相關(guān)頁(yè)面X坐標(biāo)、Y坐標(biāo)、打印區(qū)域?qū)挾?、長(zhǎng)度 intx = e.PageSettings.Margins.Left; inty = e.PageSettings.Margins.Top; intwidth = e.PageSettings.PaperSize.Width -e.PageSettings.Margins.Left- e.PageSettings.Margins.Right; intheight = e.PageSettings.PaperSize.Height -e.PageSettings.Margins.Top -e.PageSettings.Margins.Bottom; //定義打印字體 Fontfont = new Font("宋體",15); //rowCount是除去打印過(guò)的行數(shù)后剩下的行數(shù) introwCount = dt.Rows.Count-count; //maxPageRow是當(dāng)前設(shè)置下該頁(yè)面可以打印的最大行數(shù) intmaxPageRow=height/(int)font.GetHeight(); //因?yàn)槭潜砀?,先畫一條水平直線 g.DrawLine(newPen(Brushes.Black, 1), new Point(x, y), new Point(x +(dt.Columns.Count) * 135, y)); //再畫出表格各列的列標(biāo)題 for(int i = 0; i < dt.Columns.Count; i++) { stringhead = dt.Columns[i].ColumnName; g.DrawString(head,font, Brushes.Black, x + i * 135, y); } //畫完標(biāo)題,再畫一條直線 g.DrawLine(newPen(Brushes.Black, 1), new Point(x, y + (int)font.GetHeight()), newPoint(x + (dt.Columns.Count) * 135, y +(int)font.GetHeight())); //判斷,如果剩下的行數(shù)小于可打印的最大行數(shù),則執(zhí)行下列代碼 if(maxPageRow >=rowCount) { //當(dāng)前行數(shù)小于Table內(nèi)總行數(shù)時(shí),循環(huán) while(count < dt.Rows.Count) { //內(nèi)循環(huán)打印Table內(nèi)行數(shù)據(jù) intcolumnCount = 0; while(columnCount < dt.Columns.Count) { stringtemp = dt.Rows[count][columnCount].ToString(); //打印每個(gè)單元格內(nèi)的數(shù)據(jù) g.DrawString(temp,font, Brushes.Black, x + columnCount * 135, y + (count %maxPageRow) * (int)font.GetHeight() +(int)font.GetHeight()); //打印完一行后,繼續(xù)打印一條直線 g.DrawLine(newPen(Brushes.Black, 1), new Point(x, y + (count % maxPageRow) *(int)font.GetHeight() + 2 * (int)font.GetHeight()), new Point(x +(dt.Columns.Count) * 135, y + (count % maxPageRow) *(int)font.GetHeight() + 2 * (int)font.GetHeight())); columnCount++; } count++; } //所有數(shù)據(jù)打印完畢后,打印垂直直線 for (int i = 0; i <= dt.Columns.Count; i++) { g.DrawLine(newPen(Brushes.Black), new Point(x + i * 135, y), new Point(x + i *135, y + rowCount * (int)font.GetHeight() +(int)font.GetHeight())); } } //判斷,如果剩下的行數(shù)大于可打印的最大行數(shù),則執(zhí)行下列代碼 else { do { //與上面類似,注意下面while的條件 intcolumnCount = 0; while(columnCount < dt.Columns.Count) { stringtemp = dt.Rows[count][columnCount].ToString(); //打印每個(gè)單元格 g.DrawString(temp,font, Brushes.Black, x + columnCount * 135, y + (count %maxPageRow) * (int)font.GetHeight() +(int)font.GetHeight()); //打印水平直線 g.DrawLine(newPen(Brushes.Black, 1), new Point(x, y + (count % maxPageRow) *(int)font.GetHeight() + 2 * (int)font.GetHeight()), new Point(x +(dt.Columns.Count)*135, y + (count % maxPageRow) *(int)font.GetHeight() + 2 * (int)font.GetHeight())); columnCount++; } count++; }while ((count % maxPageRow >0)); //打印垂直直線 for(int i = 0; i <= dt.Columns.Count;i++ ) { g.DrawLine(newPen(Brushes.Black), new Point(x + i * 135, y), new Point(x + i *135, y + height + (int)font.GetHeight())); } } //指定HasMorePages值,如果頁(yè)面最大行數(shù)小于剩下的行數(shù),則返回true(還有),否則返回false if(maxPageRow<rowCount) { e.HasMorePages= true; } else { e.HasMorePages= false; count= 0; } } 奇長(zhǎng)無(wú)比,打印的原理就是畫,所有東西都是畫出來(lái)的,大體思路就是這樣,其他的無(wú)非就是計(jì)算跟調(diào)整間距問(wèn)題,多試幾次不難,我覺(jué)得最難的部分是循環(huán)取值及設(shè)置HasMorePages屬性,PrintDocument的PrintPage方法是打印完一頁(yè)調(diào)用一次,因此當(dāng)自己做打印設(shè)置時(shí),需要清楚的知道哪些變量需要儲(chǔ)存為全局變量以方便記錄打印位置,HasMorePages屬性也需要注意,是不是只需要一個(gè)條件就可以指定其值,很多情況下需要多個(gè)條件同時(shí)滿足,另外對(duì)畫表格這種事情來(lái)說(shuō),前幾頁(yè)跟最后一頁(yè)while里條件寫法肯定是不同的,寫一起就亂套了,最后,在給HasMorePages設(shè)置為false后,不要忘記把你的全局變量恢復(fù)初始值,否則連點(diǎn)兩下打印預(yù)覽就沒(méi)數(shù)據(jù)了,以上是A4大小,如果只實(shí)現(xiàn)簡(jiǎn)單打印,這樣足夠了,如果設(shè)計(jì)到調(diào)整頁(yè)面,設(shè)置字體等等,抱歉,做不了,這就需要網(wǎng)上DOWN現(xiàn)成的了。下一個(gè)說(shuō)說(shuō)TextBox里的文本打印。 示例二:文本打印//設(shè)置全局變量保存截取字符串位置int sub=0;private void printText(stringtext, System.Drawing.Printing.PrintPageEventArgs e) { //取得Graphics實(shí)例 Graphics g = e.Graphics; //獲得相關(guān)點(diǎn)坐標(biāo)、長(zhǎng)度、寬度 int x = e.PageSettings.Margins.Left; int y = e.PageSettings.Margins.Right; int width =e.PageSettings.PaperSize.Width-e.PageSettings.Margins.Left-e.PageSettings.Margins.Right; int height =e.PageSettings.PaperSize.Height-e.PageSettings.Margins.Top-e.PageSettings.Margins.Bottom; //設(shè)置字體 Font font=new Font("宋體",15); //這個(gè)方法后面講 g.MeasureString(text.Substring(sub), font, new SizeF(width,height-10), new StringFormat(), out charnum, out line); //打印string g.DrawString(text.Substring(sub), font, Brushes.Black, newRectangleF(x, y, width, height), newStringFormat()); //設(shè)置截取位置 sub += charnum; //設(shè)置HasMorePage屬性 if (sub < this.txtText.Text.Length) { e.HasMorePages = true; } else { e.HasMorePages = false; sub=0; } } 注意問(wèn)題有這么幾點(diǎn),MeasureString方法單獨(dú)拿出來(lái)說(shuō),此方法的通俗理解就是,把一個(gè)字符串按照指定的矩形區(qū)域、字符串格式、字體選取,并在選取后將字符數(shù)量,所需行數(shù)保存在兩個(gè)outint變量中,第一個(gè)參數(shù),需要操作的字符串;第二個(gè),字體;第三個(gè),區(qū)域;第四個(gè),字符串的格式化樣式;第五個(gè),字符個(gè)數(shù);第六個(gè),所需行數(shù)。注意的是兩個(gè)outint類型的參數(shù),這里跟ref類型差不多,當(dāng)傳遞參數(shù)的時(shí)候,實(shí)際傳遞的是參數(shù)的地址,因此在方法內(nèi)部對(duì)參數(shù)做了修改之后,會(huì)反應(yīng)到源數(shù)據(jù)上,因此在執(zhí)行完這個(gè)方法后,你所傳遞進(jìn)來(lái)的兩個(gè)參數(shù)已經(jīng)保存了相關(guān)信息。DrawString方法也使用了其中一個(gè)帶有RectangleF參數(shù)的重載方法,目的是與MeasureString相吻合,注意我劃線幾個(gè)地方的寫法,注意MeasureString中第一個(gè)參數(shù)string同樣需要截取。 兩個(gè)例子說(shuō)完了,下面說(shuō)說(shuō)怎么調(diào)用打印方法://打印預(yù)覽private voidbutton2_Click(object sender, EventArgs e) { //注意指定其Document屬性 this.printPreviewDialog1.Document = this.printDocument1; this.printPreviewDialog1.ShowDialog(); } //打印 private voidbutton1_Click(object sender, EventArgs e) { //同樣注意指定Document屬性 this.printDialog1.Document = this.printDocument1; if (this.printDialog1.ShowDialog() == DialogResult.OK) { //觸發(fā)PrintDocument的PrintPage事件 this.printDocument1.Print(); } }