廢話不多說,直接上代碼,代碼內有詳細注釋:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Printing;
using System.Windows.Forms;
using System.Reflection;
using System.Data;
using System.IO;
namespace Common
{
/// <summary>
/// 通用打印模型(T-->列表數據的模型,該模型必須為自定義的對象模型,嚴禁使用linq或其他工具生成的數據庫表模型,其中的各個字段均需要使用string類型)
/// </summary>
/// <typeparam name="T">列表數據的模型,該模型必須為自定義的對象模型,嚴禁使用linq或其他工具生成的數據庫表模型,其中的各個字段均需要使用string類型</typeparam>
public class PrintDataModel<T>
{
/// <summary>
/// 文檔標題
/// </summary>
public string pageTitle { get; set; }
/// <summary>
/// 圖形數據
/// </summary>
public Bitmap extData { get; set; }
/// <summary>
/// 頭部的表頭數據(不包括文檔標題,如果沒有,應傳入 null;每個元素為一行,如果一行無法容納,會換行處理。)
/// </summary>
public List<string> TitleData { get; set; }
/// <summary>
/// 頁面中間的列表數據(如果沒有,應傳入 null)
/// </summary>
public List<T> TableData { get; set; }
/// <summary>
/// 頁面中間的列表數據的表頭(與列表數據中的列一一對應,如果沒有(僅限列表數據傳入null的情況),應傳入 null)
/// </summary>
public List<string> ColumnNames { get; set; }
/// <summary>
/// 列表數據的各個列是否允許折行顯示,允許為true,不允許為false(僅當自動計算的列寬不足以顯示內容時生效,如果傳入null則表示所有列均允許折行顯示)
/// </summary>
public List<bool> CanResetLine { get; set; }
/// <summary>
/// 底部的結尾數據(不包括頁碼,如果沒有,應傳入 null;每個元素為一行,如果一行無法容納,會換行處理。)
/// </summary>
public List<string> EndData { get; set; }
}
/// <summary>
/// 通用打印類(調用本類的方法是,請使用try-catch語句塊,內部錯誤消息將以異常的形式拋出)
/// </summary>
/// <typeparam name="T">列表數據的模型,該模型必須為自定義的對象模型,嚴禁使用linq或其他工具生成的數據庫表模型,其中的各個字段均需要使用string類型</typeparam>
public class CommonPrintTools<T>
{
private PrintDocument docToPrint = new System.Drawing.Printing.PrintDocument();//創建一個PrintDocument的實例
/// <summary>
/// 需打印的文檔數據
/// </summary>
private List<PrintDataModel<T>> _printDataModels;
/// <summary>
/// 數據打印時的頁碼
/// </summary>
private int pageIndex = 1;
/// <summary>
/// 需要打印的文檔繪制出的圖片數據
/// </summary>
private List<Bitmap> _printBmps = new List<Bitmap>();
private int count = 0;
/// <summary>
/// 初始化打印類的各項參數
/// </summary>
/// <param name="PrintDataModels">需打印的文檔數據</param>
public CommonPrintTools(List<PrintDataModel<T>> PrintDataModels)
{
this.docToPrint.PrintPage += new PrintPageEventHandler(docToPrint_PrintPage);//將事件處理函數添加到PrintDocument的PrintPage中
_printDataModels = PrintDataModels;
}
/// <summary>
/// 打印機開始打印的事件處理函數
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void docToPrint_PrintPage(object sender, PrintPageEventArgs e)
{
Font f = new Font("宋體", 10, FontStyle.Regular);
decimal Chinese_OneWidth = Convert.ToDecimal(e.Graphics.MeasureString("測", f).Width);
int pageWidth = Convert.ToInt32(Math.Round(e.PageSettings.PrintableArea.Width, 0)) - 1;//打印機可打印區域的寬度
int onePageHeight = Convert.ToInt32(Math.Round(e.PageSettings.PrintableArea.Height, 0)) - 1;//打印機可打印區域的高度
if (_printBmps == null || _printBmps.Count <= 0)
_printBmps = DrawPrintPic(e.Graphics, pageWidth, onePageHeight);
e.Graphics.DrawImage(_printBmps[count], new Rectangle((int)Math.Ceiling(Chinese_OneWidth), 0, _printBmps[count].Width, _printBmps[count].Height));
/********************start--判斷是否需要再打印下一頁--start*************************/
count++;
if (_printBmps.Count > count)
e.HasMorePages = true;
else
e.HasMorePages = false;
/**********************end--判斷是否需要再打印下一頁--end*************************/
}
/// <summary>
/// 繪制需打印的內容
/// </summary>
/// <param name="eventG"></param>
/// <param name="pageWidth"></param>
/// <param name="allPageHeight"></param>
/// <param name="msg"></param>
/// <returns></returns>
private List<Bitmap> DrawPrintPic(Graphics eventG, int pageWidth, int pageHeight)
{
Font f = new Font("宋體", 10, FontStyle.Regular);
decimal Chinese_OneWidth = Convert.ToDecimal(eventG.MeasureString("測", f).Width);
decimal Chinese_OneHeight = Convert.ToDecimal(eventG.MeasureString("測", f).Height);
decimal English_OneWidth = Convert.ToDecimal(eventG.MeasureString("c", f).Width);
decimal English_OneHeight = Convert.ToDecimal(eventG.MeasureString("c", f).Height);
List<Bitmap> bmpList = new List<Bitmap>();
//循環需打印的文檔集合
foreach (var pdm in _printDataModels)
{
Bitmap bp = new Bitmap(pageWidth, pageHeight);
Graphics g = Graphics.FromImage(bp);
//填上底色
g.FillRectangle(Brushes.White, 0, 0, bp.Width, bp.Height);
/************************start初始化每個文檔的數據start************************/
//頁面中間的列表數據
List<T> middleData = pdm.TableData;
//頁面中間的列表數據的表頭
List<string> columnNames = pdm.ColumnNames;
//頭部的表頭數據(不包括文檔標題)
List<string> topData = pdm.TitleData;
//底部的結尾數據(不包括頁碼)
List<string> bottomData = pdm.EndData;
//文檔標題
string pageTitle = pdm.pageTitle;
//各個列是否允許折行
List<bool> CanResetLine = pdm.CanResetLine;
//圖形數據
Bitmap codeBP = pdm.extData;
//檢查數據列表的列與列明是否對應
if (middleData != null && middleData.Count > 0)
{
System.Reflection.PropertyInfo[] pInfo = middleData[0].GetType().GetProperties();
if (pInfo.Length != columnNames.Count)
{
throw new Exception("列表數據的列數與表頭數據的列數不相符!");
}
}
/**************************end初始化每個文檔的數據end**************************/
/*********************start計算各部分高度以及頁面數據start*********************/
//計算表頭高度
decimal headEndHeight = ComputeHeadEndHeight(pageTitle, topData, Chinese_OneHeight, pageWidth, g, f);
//計算結尾高度
headEndHeight += ComputeHeadEndHeight(null, bottomData, Chinese_OneHeight, pageWidth, g, f);
//如果列表中有數據,並且表頭部分+一行空行的高度大於等於頁面高度(頁面中沒有剩余空間繪制列表數據),拋出異常信息
if ((headEndHeight + Chinese_OneHeight >= pageHeight) && middleData != null && middleData.Count > 0)
{
throw new Exception("表頭或表尾數據太多,頁面中將沒有繪制數據行的空間!");
}
//數據列表的各列寬度
decimal[] columnWidths = null;
//數據列表的各行行高
decimal[] rowHeights = null;
DataTable dt = new DataTable();
if (middleData != null && middleData.Count > 0)
{
string msg = "";
dt = ReflactionToDataTable(middleData, columnNames, ref msg);
if (dt == null && !string.IsNullOrEmpty(msg))
{
throw new Exception(msg);
}
columnWidths = GetColumnWidths(dt, CanResetLine, Chinese_OneWidth, g, f);
columnWidths = GetColumnWidthToPage(columnWidths, pageWidth, Chinese_OneWidth, CanResetLine, g, f);
rowHeights = GetRowHeights(dt, columnWidths, g, f);
}
/***********************end計算各部分高度以及頁面數據end***********************/
/****start繪制繪制繪制繪制繪制繪制繪制繪制繪制繪制繪制繪制繪制繪制繪制start****/
//中間數據列表存在的情況
if (middleData != null && middleData.Count > 0 && dt != null && dt.Rows.Count > 0)
{
bmpList.AddRange(DrawPage(pageWidth - (int)Math.Ceiling(Chinese_OneWidth * 2), pageHeight, f, Chinese_OneWidth, Chinese_OneHeight, topData, bottomData, pageTitle, codeBP, CanResetLine, rowHeights, dt, headEndHeight));
}
else//沒有中間數據列表的情況
{
bmpList.AddRange(DrawPageWithOutDataTable(pageWidth - (int)Math.Ceiling(Chinese_OneWidth * 2), pageHeight, f, Chinese_OneHeight, topData, bottomData, pageTitle));
}
/******end繪制繪制繪制繪制繪制繪制繪制繪制繪制繪制繪制繪制繪制繪制繪制end******/
}
return bmpList;
}
/// <summary>
/// 繪制打印頁面
/// </summary>
/// <param name="pageWidth">頁面寬度</param>
/// <param name="pageHeight">頁面高度</param>
/// <param name="f">字體</param>
/// <param name="Chinese_OneWidth">單字寬度</param>
/// <param name="Chinese_OneHeight">單字高度</param>
/// <param name="topData">頭部的表頭數據(不包括文檔標題)</param>
/// <param name="bottomData">底部的結尾數據(不包括頁碼)</param>
/// <param name="pageTitle">文檔標題</param>
/// <param name="columnWidths">列寬度集合</param>
/// <param name="rowHeights">行高集合</param>
/// <param name="dt">需打印的所有列表數據</param>
/// <returns>所有需要打印的頁面</returns>
private List<Bitmap> DrawPage(int pageWidth, int pageHeight, Font f, decimal Chinese_OneWidth, decimal Chinese_OneHeight, List<string> topData, List<string> bottomData, string pageTitle, Bitmap codeBP, List<bool> CanResetLine, decimal[] rowHeights, DataTable dt, decimal headEndHeight)
{
List<Bitmap> bmpList = new List<Bitmap>();
//獲取每頁中需要打印的列表數據
List<DataTable> dts = GetDataTableToPages(dt, rowHeights, pageHeight - headEndHeight - Chinese_OneHeight);
//未分割DataTable時DataTable的行索引
int sourceRowIndex = 1;
//未分割DataTable時DataTable的行索引(用於計算)
int sourceRowIndexForCompute = 1;
//頁索引
int pageIndex = 1;
//循環繪制每一頁
foreach (DataTable drawDt in dts)
{
Bitmap bpItem = new Bitmap(pageWidth, pageHeight);
Graphics gItem = Graphics.FromImage(bpItem);
//當前顯示的數據列表的各列寬度
decimal[] columnWidths = GetColumnWidths(drawDt, CanResetLine, Chinese_OneWidth, gItem, f);
columnWidths = GetColumnWidthToPage(columnWidths, pageWidth, Chinese_OneWidth, CanResetLine, gItem, f);
//填上底色
gItem.FillRectangle(Brushes.White, 0, 0, bpItem.Width, bpItem.Height);
//當前繪制行距離頁面最頂部的距離
decimal currentMarginTop = 0;
/******************************start表頭部分start******************************/
//標題
if (!string.IsNullOrEmpty(pageTitle))
{
currentMarginTop += Chinese_OneHeight;
List<string> drawValues = GetMultiLineString(pageTitle, pageWidth, gItem, f);
foreach (string dv in drawValues)
{
gItem.DrawString(dv, f, Brushes.Black, (float)(pageWidth - (decimal)gItem.MeasureString(dv, f).Width) / 2, (float)currentMarginTop);
currentMarginTop += Chinese_OneHeight;
}
}
//圖形
if (codeBP != null && codeBP.Height > 0 && codeBP.Width > 0)
{
gItem.DrawImage(codeBP, 0, (float)currentMarginTop);
currentMarginTop += codeBP.Height;
}
//表頭數據
if (topData != null && topData.Count > 0)
{
foreach (string tdTitle in topData)
{
currentMarginTop += Chinese_OneHeight/2;
List<string> drawValues = GetMultiLineString(tdTitle, pageWidth, gItem, f);
foreach (string dv in drawValues)
{
gItem.DrawString(dv, f, Brushes.Black, 0, (float)currentMarginTop);
currentMarginTop += Chinese_OneHeight;
}
}
}
/********************************end表頭部分end********************************/
/****************************start中間列表部分start****************************/
//繪制標題行
//每個單元格距離左側的距離
decimal colLeft = 0;
decimal drawDtHeight = rowHeights[0];//GetRowHeights(drawDt, Chinese_OneHeight, columnWidths, gItem, f).Sum();
for (int rowIndex = 0; rowIndex < drawDt.Rows.Count; rowIndex++)
{
drawDtHeight += rowHeights[sourceRowIndexForCompute];
sourceRowIndexForCompute++;
}
currentMarginTop += Chinese_OneHeight / 2;
//繪制各個列的標題
for (int colIndex = 0; colIndex < drawDt.Columns.Count; colIndex++)
{
//列名
string colName = drawDt.Columns[colIndex].ColumnName;
//計算需要繪制的文字需要在幾行中顯示
List<string> drawValues = GetMultiLineString(colName, (float)columnWidths[colIndex], gItem, f);
decimal rowMarginTop = currentMarginTop;
foreach (string dv in drawValues)
{
gItem.DrawString(dv, f, Brushes.Black, (float)colLeft, (float)rowMarginTop);
rowMarginTop += Chinese_OneHeight;
}
//繪制表格的列
gItem.DrawLine(new Pen(Brushes.Black), new Point((int)Math.Ceiling(colLeft), (int)Math.Floor(currentMarginTop) - 1), new Point((int)Math.Ceiling(colLeft), (int)Math.Ceiling(currentMarginTop + drawDtHeight) - 2));
//每個單元格的數據需要向右移動一個當前單元格的寬度
colLeft += columnWidths[colIndex];
}
//繪制表格的第一行
gItem.DrawLine(new Pen(Brushes.Black), new Point(0, (int)Math.Floor(currentMarginTop) - 1), new Point((int)Math.Ceiling(columnWidths.Sum() - 2), (int)Math.Floor(currentMarginTop) - 1));
//繪制表格的最后一列
gItem.DrawLine(new Pen(Brushes.Black), new Point((int)Math.Ceiling(columnWidths.Sum() - 2), (int)Math.Floor(currentMarginTop) - 1), new Point((int)Math.Ceiling(columnWidths.Sum() - 2), (int)Math.Ceiling(currentMarginTop + drawDtHeight) - 2));
currentMarginTop += rowHeights[0];
//繪制列表中的數據
for (int rowIndex = 0; rowIndex < drawDt.Rows.Count; rowIndex++)
{
//每個單元格距離左側的距離
decimal rowMarginLeft = 0;
for (int colIndex = 0; colIndex < dt.Columns.Count; colIndex++)
{
//需要繪制的當前單元格的文字
string drawValue = drawDt.Rows[rowIndex][colIndex].ToString();
//計算需要繪制的文字需要在幾行中顯示
List<string> drawValues = GetMultiLineString(drawValue, (float)columnWidths[colIndex], gItem, f);
//當前行的各個列均是從同一個高度(距離頂部的距離)開始繪制的
decimal rowMarginTop = currentMarginTop;
foreach (string dv in drawValues)
{
gItem.DrawString(dv, f, Brushes.Black, (float)rowMarginLeft, (float)rowMarginTop);
rowMarginTop += Chinese_OneHeight;
}
//每個單元格的數據需要向右移動一個當前單元格的寬度
rowMarginLeft += columnWidths[colIndex];
}
//繪制表格的行
gItem.DrawLine(new Pen(Brushes.Black), new Point(0, (int)Math.Floor(currentMarginTop) - 1), new Point((int)Math.Ceiling(columnWidths.Sum() - 2), (int)Math.Floor(currentMarginTop) - 1));
//繪制完一行的數據后,將高度(距離頂部的距離)累加當前行的高度
currentMarginTop += rowHeights[sourceRowIndex];
sourceRowIndex++;
}
//繪制表格的行
gItem.DrawLine(new Pen(Brushes.Black), new Point(0, (int)Math.Floor(currentMarginTop) - 1), new Point((int)Math.Ceiling(columnWidths.Sum() - 2), (int)Math.Floor(currentMarginTop) - 1));
/******************************end中間列表部分end******************************/
/******************************start結尾部分start******************************/
if (bottomData != null && bottomData.Count > 0)
{
currentMarginTop += Chinese_OneHeight / 2;
foreach (string bdData in bottomData)
{
//當前行的各個列均是從同一個高度(距離頂部的距離)開始繪制的
decimal rowMarginTop = currentMarginTop;
//計算需要繪制的文字需要在幾行中顯示
List<string> drawValues = GetMultiLineString(bdData, (float)pageWidth, gItem, f);
foreach (string dv in drawValues)
{
gItem.DrawString(dv, f, Brushes.Black, 0, (float)rowMarginTop);
rowMarginTop += Chinese_OneHeight;
}
currentMarginTop += Chinese_OneHeight;
}
}
string pageInfo = string.Format("當前第{0}頁,共{1}頁", pageIndex.ToString(), dts.Count.ToString());
gItem.DrawString(pageInfo, f, Brushes.Black, (float)(pageWidth - (decimal)gItem.MeasureString(pageInfo, f).Width - Chinese_OneWidth), (float)currentMarginTop);
/********************************end結尾部分end********************************/
bmpList.Add(bpItem);
pageIndex++;
}
return bmpList;
}
/// <summary>
/// 繪制打印頁面(無數據列表)
/// </summary>
/// <param name="pageWidth"></param>
/// <param name="pageHeight"></param>
/// <param name="f"></param>
/// <param name="Chinese_OneWidth"></param>
/// <param name="Chinese_OneHeight"></param>
/// <param name="topData"></param>
/// <param name="bottomData"></param>
/// <param name="pageTitle"></param>
/// <returns></returns>
private List<Bitmap> DrawPageWithOutDataTable(int pageWidth, int pageHeight, Font f, decimal Chinese_OneHeight, List<string> topData, List<string> bottomData, string pageTitle)
{
List<Bitmap> bmpList = new List<Bitmap>();
//所有需要繪制的數據(不包括標題)
List<string> drawList = new List<string>();
if (topData != null && topData.Count > 0)
drawList.AddRange(topData);
if (bottomData != null && bottomData.Count > 0)
drawList.AddRange(bottomData);
Bitmap bp = new Bitmap(pageWidth, pageHeight);
Graphics g = Graphics.FromImage(bp);
//可用繪制數據的高度
decimal dataHeight = pageHeight - Chinese_OneHeight;//默認為頁面高度減去一個空行高度
if (!string.IsNullOrEmpty(pageTitle))
{
//標題的高度
decimal titleHeight = 0;
//標題切割成的多行
List<string> titles = GetMultiLineString(pageTitle, pageWidth, g, f);
//計算標題的總高度
foreach (string t in titles)
{
titleHeight += (decimal)g.MeasureString(t, f).Width;
}
//計算可用繪制數據的高度(頁面高度減去標題高度再減去兩個空行的高度)
dataHeight = (decimal)pageHeight - titleHeight - Chinese_OneHeight;
}
//每頁需要繪制的數據集合
List<List<string>> drawData = GetLinesToPages(drawList, Chinese_OneHeight, dataHeight, pageWidth, g, f);
//循環繪制每頁的數據
foreach (List<string> drawD in drawData)
{
Bitmap bpItem = new Bitmap(pageWidth, pageHeight);
Graphics gItem = Graphics.FromImage(bpItem);
//填上底色
gItem.FillRectangle(Brushes.White, 0, 0, bpItem.Width, bpItem.Height);
//當前繪制行距離頁面最頂部的距離
decimal currentMarginTop = 0;
//標題
if (!string.IsNullOrEmpty(pageTitle))
{
currentMarginTop += Chinese_OneHeight;
List<string> drawValues = GetMultiLineString(pageTitle, pageWidth, gItem, f);
foreach (string dv in drawValues)
{
gItem.DrawString(dv, f, Brushes.Black, (float)(pageWidth - (decimal)gItem.MeasureString(dv, f).Width) / 2, (float)currentMarginTop);
currentMarginTop += Chinese_OneHeight;
}
}
foreach (string dv in drawD)
{
gItem.DrawString(dv, f, Brushes.Black, 0, (float)currentMarginTop);
currentMarginTop += Chinese_OneHeight;
}
string pageInfo = string.Format("當前第{0}頁,共{1}頁", pageIndex.ToString(), drawData.Count.ToString());
gItem.DrawString(pageInfo, f, Brushes.Black, 0, (float)currentMarginTop);
bmpList.Add(bpItem);
pageIndex++;
}
return bmpList;
}
/// <summary>
/// 開始打印
/// </summary>
/// <param name="printname">計算機上已安裝的打印機的名稱</param>
public void StartPrint(string printname)
{
System.Windows.Forms.PrintDialog PrintDialog1 = new PrintDialog();//創建一個PrintDialog的實例。
PrintDialog1.AllowSomePages = true;
PrintDialog1.ShowHelp = true;
PrintDialog1.Document = docToPrint;//把PrintDialog的Document屬性設為上面配置好的PrintDocument的實例
docToPrint.PrinterSettings.PrinterName = printname; //_366KF.Manage.Common.PickOrderPrinter; //設置打印機,填寫計算機上已安裝的打印機的名稱
docToPrint.Print();//開始打印
}
/// <summary>
/// 打印預覽
/// </summary>
/// <param name="dt">要打印的DataTable</param>
/// <param name="Title">打印文件的標題</param>
public void PrintPriview()
{
try
{
PrintPreviewDialog PrintPriview = new PrintPreviewDialog();
PrintPriview.Document = docToPrint;
PrintPriview.WindowState = FormWindowState.Maximized;
PrintPriview.ShowDialog();
}
catch (Exception ex)
{
MessageBox.Show("打印錯誤,請檢查打印設置!消息:" + ex.Message);
}
}
public void TestPrint()
{
int pageWidth = 300; //Convert.ToInt32(Math.Round(e.PageSettings.PrintableArea.Width, 0)) - 1;//打印機可打印區域的寬度
int onePageHeight = 1000;// Convert.ToInt32(Math.Round(e.PageSettings.PrintableArea.Height, 0)) - 1;//打印機可打印區域的高度
Bitmap bp = new Bitmap(pageWidth, onePageHeight);
Graphics g = Graphics.FromImage(bp);
List<Bitmap> bmps = DrawPrintPic(g, pageWidth, onePageHeight);
int bmpName = 1;
string path = Application.StartupPath + "/pic";
List<string> paths = Directory.GetFiles(path).ToList();
foreach (string p in paths)
{
File.Delete(p);
}
foreach (Bitmap b in bmps)
{
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
b.Save(path + "/" + bmpName + ".jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
bmpName++;
}
}
/// <summary>
/// 利用反射將數據對象集合轉換為DataTable
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="dataModel"></param>
/// <param name="columnNames"></param>
/// <returns></returns>
private DataTable ReflactionToDataTable<T>(List<T> dataModel, List<string> columnNames, ref string msg)
{
try
{
DataTable dt = new DataTable();
if (typeof(T).Equals(typeof(String)))
{
msg = "數據集合必須為自定義對象集合!";
return new DataTable();
}
if (dataModel == null || dataModel.Count <= 0)
{
msg = "傳入的數據集合為空!";
return new DataTable();
}
if (columnNames == null || columnNames.Count <= 0)
{
msg = "傳入的列名數據集合為空!";
return new DataTable();
}
PropertyInfo[] pInfos = ((object)dataModel[0]).GetType().GetProperties();
if (pInfos.Length != columnNames.Count)
{
msg = "數據列數與列名數量不一致!";
return new DataTable();
}
for (int i = 0; i < columnNames.Count; i++)
{
dt.Columns.Add(columnNames[i], pInfos[i].PropertyType);
}
for (int i = 0; i < dataModel.Count; i++)
{
object[] objArray = new object[pInfos.Length];
for (int j = 0; j < pInfos.Length; j++)
{
object ob = pInfos[j].GetValue(dataModel[i], null);
objArray.SetValue(ob, j);
}
dt.LoadDataRow(objArray, true);
}
return dt;
}
catch (Exception ex)
{
msg = "請檢查傳入的數據類型,是否為List<自定義對象>類型,自定義對象必須有屬性值!(異常信息:" + ex.Message + ")";
return null;
}
}
/// <summary>
/// 講一個字符串按照固定的長度切分為多個適合長度的字符串
/// </summary>
/// <param name="dataLine">需要切分的字符串</param>
/// <param name="width">固定的長度</param>
/// <param name="g">繪制對象</param>
/// <param name="f">字體</param>
/// <returns>切分后得到的字符串集合</returns>
private List<string> GetMultiLineString(string dataLine, float width, Graphics g, Font f)
{
List<string> dataLines = new List<string>();
char[] chars = dataLine.ToCharArray();
decimal widthT = Convert.ToDecimal(width);
decimal charWidth = Convert.ToDecimal(g.MeasureString("c".ToString(), f).Width);
if (widthT < charWidth)
{
throw new Exception("數據無法正常顯示,經優化計算后的列寬不足以存放一個字符!");
}
decimal widthC = 0;
int i = 0;
string dLine = "";
string tmpLine = "";
while (true)
{
if (i == chars.Length)
widthC = decimal.MaxValue;
else
{
tmpLine += chars[i].ToString();
widthC = Convert.ToDecimal(g.MeasureString(tmpLine, f).Width);
}
if (widthC < widthT)
{
dLine = tmpLine;
}
else
{
dataLines.Add(dLine);
widthC = 0;
dLine = "";
tmpLine = "";
if (i < chars.Length)
i--;
}
if (i >= chars.Length)
break;
i++;
}
return dataLines;
}
/// <summary>
/// 獲取各個列的最大寬度值
/// </summary>
/// <param name="DataTablePrint">數據列表</param>
/// <param name="CanResetLine">各列是否允許折行顯示</param>
/// <param name="Chinese_OneWidth">單字寬度</param>
/// <param name="g">繪制對象</param>
/// <param name="f">字體</param>
/// <returns></returns>
private decimal[] GetColumnWidths(DataTable DataTablePrint, List<bool> CanResetLine, decimal Chinese_OneWidth, Graphics g, Font f)
{
decimal[] res = new decimal[DataTablePrint.Columns.Count]; ;
foreach (DataRow dr in DataTablePrint.Rows)
{
for (int i = 0; i < DataTablePrint.Columns.Count; i++)
{
//后面加的半個單字寬度用來抵消decimal類型尾數被舍棄的情形
decimal colwidth = Convert.ToDecimal(g.MeasureString(dr[i].ToString().Trim(), f).Width) + (CanResetLine[i] ? 0 : Chinese_OneWidth / 2);
if (colwidth > res[i])
{
res[i] = colwidth;
}
}
}
for (int Cols = 0; Cols <= DataTablePrint.Columns.Count - 1; Cols++)
{
string ColumnText = DataTablePrint.Columns[Cols].ColumnName.ToString();
//后面加的半個單字寬度用來抵消decimal類型尾數被舍棄的情形
decimal colwidth = Convert.ToInt32(g.MeasureString(ColumnText, f).Width) + (CanResetLine[Cols] ? 0 : Chinese_OneWidth / 2);
if (colwidth > res[Cols])
{
res[Cols] = colwidth;
}
}
return res;
}
/// <summary>
/// 計算表頭部分高度
/// </summary>
/// <param name="pageTitle">頁面標題</param>
/// <param name="topData">表頭數據</param>
/// <param name="Chinese_OneHeight">單行高度</param>
/// <param name="pageWidth">頁面寬度</param>
/// <param name="g">繪制對象</param>
/// <param name="f">字體</param>
/// <returns></returns>
private decimal ComputeHeadEndHeight(string pageTitle, List<string> topData, decimal Chinese_OneHeight, float pageWidth, Graphics g, Font f)
{
decimal headHeight = 0;
if (!string.IsNullOrEmpty(pageTitle))
{
List<string> pts = GetMultiLineString(pageTitle, pageWidth, g, f);
headHeight += Chinese_OneHeight;
headHeight += GetRowHeight(pts, g, f);
headHeight += Chinese_OneHeight;
}
if (topData != null && topData.Count > 0)
{
foreach (string tds in topData)
{
List<string> tdss = GetMultiLineString(tds, pageWidth, g, f);
headHeight += GetRowHeight(tdss, g, f);
}
}
return headHeight;
}
/// <summary>
/// 獲取各個行的最大高度值(索引為0的行高為列標題的最大行高)
/// </summary>
/// <param name="dt">數據列表</param>
/// <param name="columnWidths">各個列的最大寬度值</param>
/// <param name="g">繪制對象</param>
/// <param name="f">字體</param>
/// <returns>返回行最大高度值集合</returns>
private decimal[] GetRowHeights(DataTable dt, decimal[] columnWidths, Graphics g, Font f)
{
decimal[] rowHeights = new decimal[dt.Rows.Count + 1];
int columnNameIndex = 0;
//計算標題行的高度
foreach (DataColumn dc in dt.Columns)
{
string dValue = dc.ColumnName;
decimal cwidth = columnWidths[columnNameIndex];
List<string> mLines = GetMultiLineString(dValue, (float)cwidth, g, f);
decimal h = GetRowHeight(mLines, g, f);
if (h > rowHeights[0])
{
rowHeights[0] = h;
}
columnNameIndex++;
}
int columnIndex = 0;
//計算各個數據行的高度
foreach (DataColumn dc in dt.Columns)
{
for (int i = 0; i < dt.Rows.Count; i++)
{
string dValue = dt.Rows[i][dc.ColumnName].ToString();
decimal cwidth = columnWidths[columnIndex];
List<string> mLines = GetMultiLineString(dValue, (float)cwidth, g, f);
decimal h = GetRowHeight(mLines, g, f);
if (h > rowHeights[i + 1])
{
rowHeights[i + 1] = h;
}
}
columnIndex++;
}
return rowHeights;
}
/// <summary>
/// 計算行高
/// </summary>
/// <param name="lines">一個DataRow需要顯示的行集合</param>
/// <param name="g">繪制對象</param>
/// <param name="f">字體</param>
/// <returns>返回DataRow行高</returns>
private decimal GetRowHeight(List<string> lines, Graphics g, Font f)
{
decimal h = 0;
foreach (string line in lines)
{
h += (decimal)g.MeasureString(line, f).Height;
}
return h;
}
/// <summary>
/// 根據實際的列寬計算適應頁面所需的列寬度
/// </summary>
/// <param name="columnWidths">實際列寬</param>
/// <param name="pageWidth">頁面寬度</param>
/// <param name="Chinese_OneWidth">單字寬度</param>
/// <param name="CanResetLine">各列是否允許折行顯示</param>
/// <param name="g">繪制對象</param>
/// <param name="f">字體</param>
/// <returns>按比例計算之后的列寬</returns>
private decimal[] GetColumnWidthToPage(decimal[] columnWidths, decimal pageWidth, decimal Chinese_OneWidth, List<bool> CanResetLine, Graphics g, Font f)
{
string cWidthString = "";
for (int i = 0; i < columnWidths.Length; i++)
{
cWidthString += "測";
}
if (pageWidth < (decimal)g.MeasureString(cWidthString, f).Width)
{
throw new Exception("列數太多,當前紙張無法呈現(以每列一個漢字算,所有列的寬度之和,大於紙張寬度)!");
}
//允許折行的列數
int canResetCount = 0;
//不允許折行的列的總寬度
decimal solidWidth = 0;
//允許折行的列的總寬度
decimal columnSumWidth = columnWidths.Sum();
//是否存在不允許折行的列
if (CanResetLine != null && CanResetLine.Where(c => !c).Count() > 0)
{
columnSumWidth = 0;
for (int i = 0; i < columnWidths.Length; i++)
{
//如果當前列不允許折行,累加不允許折行的列的總寬度
if (!CanResetLine[i])
{
solidWidth += columnWidths[i];
}
else//累加允許折行的列的總寬度,累加允許折行的列數
{
columnSumWidth += columnWidths[i];
canResetCount++;
}
}
//計算可以進行折行處理的剩余總寬度
pageWidth -= solidWidth;
}
//如果 按單字寬度計算,允許折行的總寬度 大於(>) 可以進行折行處理的剩余總寬度,則拋出異常
string crWidthString = "";
for (int i = 0; i < canResetCount; i++)
{
crWidthString += "測";
}
if ((decimal)g.MeasureString(crWidthString, f).Width > pageWidth)
{
throw new Exception("當前紙張無法呈現所有內容(以每列一個漢字算,所有可折行列的寬度之和,大於可以進行折行處理的剩余總寬度)!");
}
decimal[] res = new decimal[columnWidths.Length];
columnWidths.CopyTo(res, 0);
for (int i = 0; i < columnWidths.Length; i++)
{
if (CanResetLine == null || CanResetLine[i])
res[i] = (res[i] / columnSumWidth) * pageWidth;
}
return res;
}
/// <summary>
/// 獲取每頁的數據列表
/// </summary>
/// <param name="dt">所有列表數據</param>
/// <param name="rowHeights">行高集合</param>
/// <param name="dataHeight">顯示數據的可用高度</param>
/// <returns></returns>
private List<DataTable> GetDataTableToPages(DataTable dt, decimal[] rowHeights, decimal dataHeight)
{
dataHeight -= rowHeights[0];
List<DataTable> dts = new List<DataTable>();
int rowIndex = 0;
decimal currentHeight = 0;
decimal[] rowHs = new decimal[rowHeights.Length - 1];
for (int i = 0; i < rowHs.Length; i++)
{
rowHs[i] = rowHeights[i + 1];
}
List<DataRow> drs = new List<DataRow>();
while (rowIndex <= dt.Rows.Count)
{
if (rowIndex == rowHs.Length)
{
DataTable ndt = dt.Clone();
foreach (DataRow dr in drs)
{
DataRow ndtDr = ndt.NewRow();
foreach (DataColumn dc in dt.Columns)
{
ndtDr[dc.ColumnName] = dr[dc.ColumnName];
}
ndt.Rows.Add(ndtDr);
}
dts.Add(ndt);
break;
}
if ((currentHeight + rowHs[rowIndex] > dataHeight))
{
DataTable ndt = dt.Clone();
foreach (DataRow dr in drs)
{
DataRow ndtDr = ndt.NewRow();
foreach (DataColumn dc in dt.Columns)
{
ndtDr[dc.ColumnName] = dr[dc.ColumnName];
}
ndt.Rows.Add(ndtDr);
}
dts.Add(ndt);
rowIndex--;
currentHeight = 0;
drs.Clear();
}
else
{
DataRow dr = dt.NewRow();
for (int i = 0; i < dt.Columns.Count; i++)
{
dr[i] = dt.Rows[rowIndex][i].ToString();
}
drs.Add(dr);
currentHeight += rowHs[rowIndex];
}
rowIndex++;
}
return dts;
}
/// <summary>
/// 獲取每頁的非數據列表的數據行
/// </summary>
/// <param name="data">所有數據行</param>
/// <param name="Chinese_OneHeight">單行高度</param>
/// <param name="dataHeight">顯示數據的可用高度</param>
/// <param name="pageWidth">頁面寬度</param>
/// <param name="g">繪制對象</param>
/// <param name="f">字體</param>
/// <returns></returns>
private List<List<string>> GetLinesToPages(List<string> data, decimal Chinese_OneHeight, decimal dataHeight, decimal pageWidth, Graphics g, Font f)
{
List<List<string>> res = new List<List<string>>();
List<string> resData = new List<string>();
decimal currentHeight = 0;
foreach (string dLine in data)
{
List<string> dvs = GetMultiLineString(dLine, (float)pageWidth, g, f);
foreach (string dv in dvs)
{
if (currentHeight + Chinese_OneHeight > dataHeight)
{
res.Add(resData);
resData.Clear();
currentHeight = 0;
resData.Add(dv);
currentHeight += Chinese_OneHeight;
}
else
{
resData.Add(dv);
currentHeight += Chinese_OneHeight;
}
}
}
return res;
}
}
}
