You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

359 lines
14 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows;
using Utils.Datas;
namespace Utils.Printers
{
public class DrawPage
{
public DrawPage()
{
_Headers = new List<Dictionary<string, string>>();
_DetailRows = new List<Dictionary<string, string>>();
}
#region 变量、属性
public double FontSize_InforsBeforeHead { get; set; }//表前信息的字号
public IList<string> InforsBeforeHead { get; set; }//表前信息
private double _PageWidth = 800;
public double PageWidth { set { _PageWidth = value; } get { return _PageWidth; } }//页宽
public string Title { get; set; }//标题
private double _TitleFontSize = 20;
public double TitleFontSize { set { _TitleFontSize = value; } get { return _TitleFontSize; } }//标题字号
private double _TitleMarginLeft = 20;
public double TitleMarginLeft { set { _TitleMarginLeft = value; } get { return _TitleMarginLeft; } }//标题左边距
private double _MarginLeft = 20;
public double MarginLeft { set { _MarginLeft = value; } get { return _MarginLeft; } }//左边距
private double _MarginTop = 20;
public double MarginTop { set { _MarginTop = value; } get { return _MarginTop; } }//顶边距
private double _LineDistance = 4;
public double LineDistance { set { _LineDistance = value; } get { return _LineDistance; } }//行距
private double _FontSize = 16;
public double FontSize { set { _FontSize = value; } get { return _FontSize; } }//字号
private double _FontSize_Footer = 10;
public double FontSize_Footer { set { _FontSize_Footer = value; } get { return _FontSize_Footer; } }//页脚字号
private Typeface _TypeFace = new Typeface("SimSun");
public Typeface TypeFace { set { _TypeFace = value; } get { return _TypeFace; } }
private int _HeaderColumnsCount = 3;
public int HeaderColumnsCount { set { _HeaderColumnsCount = value; } get { return _HeaderColumnsCount; } }//表头列数
private int _HeaderCharCount = 4;
public int HeaderCharCount { set { _HeaderCharCount = value; } get { return _HeaderCharCount; } }//表头字数
private int _DetailMaxRows = 6;
public int DetailMaxRows { set { _DetailMaxRows = value; } get { return _DetailMaxRows; } }//清单行数。超出则分页
public IList<string> SumColumnNames { get; set; }//合计列的列名
public bool AllowSum { get; set; }//是否合计
public double FontSize_InforsBeforeFooter { get; set; }
public double FontSize_InforsAfterFooter { get; set; }
public IList<string> InforsBeforeFooter { get; set; }
public IList<string> InforsAfterFooter { get; set; }
private List<Dictionary<string, string>> _Headers;
private string[] _ColumnNames;
private double[] _ColumnWidths;
private int _DefaultWidth_CharCount = 4;//默认列宽(字数)
private List<Dictionary<string, string>> _DetailRows;
public string Printer { get; set; }//打印人
public bool ShowPrinter { get; set; }//是否打印打印人
public bool ShowPrintTime { get; set; }//是否打印打印时间
public bool ShowPageNum { get; set; }//是否打印页码
#endregion
/// <summary>
/// 添加表头
/// <para>可多次添加。每次添加新起一行;一次添加的信息,如果达到最大列数,自动换行</para>
/// </summary>
public void AddHeaders(Dictionary<string, string> headers)
{
Dictionary<string, string> tmp = new Dictionary<string, string>();
foreach (var item in headers) {
tmp.Add(item.Key + ":", item.Value);
}
_Headers.Add(tmp);
}
/// <summary>
/// 添加清单列名
/// </summary>
public void AddColumnNames(params string[] columnNames)
{
_ColumnNames = columnNames;
}
/// <summary>
/// 添加列宽。如果添加的列宽,没有列数多,则默认设置为_DefaultWidth_CharCount变量对应的字数的宽度。
/// </summary>
public void SetColumnWidths(params double[] columnWidths)
{
_ColumnWidths = columnWidths;
}
/// <summary>
/// 添加一行数据。该方法使用前提:先添加列名(AddColumnNames方法)
/// </summary>
public void AddRow(params string[] values)//前提条件:先添加列名
{
#if DEBUG
if (values == null || values.Count() == 0)
throw new ApplicationException("AddRow(params string[] values)方法应该至少有一个参数");
if (_ColumnNames == null || _ColumnNames.Count() == 0)
throw new ApplicationException("请先调用AddColumnNames方法添加列名");
#endif
Dictionary<string, string> row = new Dictionary<string, string>();
for (int i = 0; i < _ColumnNames.Length; i++) {
if (values.Length > i)
row.Add(_ColumnNames.ElementAt(i), values.ElementAt(i));
}
_DetailRows.Add(row);
}
/// <summary>
/// 添加一行数据。
/// </summary>
public void AddRow(Dictionary<string, string> values)
{
#if DEBUG
if (values == null || values.Count == 0)
throw new ApplicationException("AddRow(Dictionary<string, string> values)方法添加的列至少应该有一列");
#endif
_DetailRows.Add(values);
}
/// <summary>
/// 得到打印页面
/// </summary>
/// <param name="pageIndex">页的序号,从0开始</param>
public DocumentPage GetPage(int pageIndex)
{
DrawingVisual visual = new DrawingVisual();
using (DrawingContext dc = visual.RenderOpen()) {
//打印表前信息
double nextLineStartY = _MarginTop;
if (InforsBeforeHead != null || InforsBeforeHead.Count > 0)
nextLineStartY = DrawInfos(dc, FontSize_InforsBeforeHead, _MarginLeft, nextLineStartY, InforsBeforeHead.ToArray());
//打印标题
nextLineStartY = DrawTitle(dc, _TitleMarginLeft, nextLineStartY, Title, _TitleFontSize);
//打印表头
nextLineStartY = DrawHeaders(dc, nextLineStartY);
//打印清单列名
List<double> columnWidthsAll;
var columnWidthStartXs = GetColumnWidthsStartX(MarginLeft, _ColumnWidths, _ColumnNames.Length, out columnWidthsAll);
DrawDetail(nextLineStartY, dc, _ColumnNames, columnWidthStartXs, columnWidthsAll, out nextLineStartY);
//打印清单
if (_DetailRows != null && _DetailRows.Count > 0) {
var rows = _DetailRows.Skip(pageIndex * DetailMaxRows);
for (int i = 0; i < rows.Count(); i++) {
if (i >= DetailMaxRows)
break;
var datas = PrepareRow(rows.ElementAt(i), _ColumnNames);
DrawDetail(nextLineStartY, dc, datas, columnWidthStartXs, columnWidthsAll, out nextLineStartY);
}
}
//打印合计
DrawSumRow(nextLineStartY, dc, columnWidthStartXs, columnWidthsAll, out nextLineStartY);
//打印页脚前信息
if (InforsBeforeFooter != null || InforsBeforeFooter.Count > 0)
nextLineStartY = DrawInfos(dc, FontSize_InforsBeforeFooter, _MarginLeft, nextLineStartY, InforsBeforeFooter.ToArray());
//打印页脚
nextLineStartY = DrawFooter(dc, nextLineStartY, pageIndex);
//打印页脚后信息
if (InforsAfterFooter != null || InforsAfterFooter.Count > 0)
nextLineStartY = DrawInfos(dc, FontSize_InforsAfterFooter, _MarginLeft, nextLineStartY, InforsAfterFooter.ToArray());
}
return new DocumentPage(visual);
}
private double DrawTitle(DrawingContext dc, double startX, double startY, string Title, double titleFontSize)
{
double nextLineStartY;
Point point = new Point(startX, startY);
PrinterUtil.DrawText(point, dc, Title, titleFontSize, LineDistance, TypeFace, out nextLineStartY);
return nextLineStartY;
}
private double DrawHeaders(DrawingContext dc, double startY)
{
double next = startY;
foreach (var item in _Headers) {
next = DrawPairsInfo(dc, item, FontSize, HeaderCharCount, HeaderColumnsCount, next);
}
return next;
}
private static List<double> GetAverageWidthsStartX(double width, double marginLeft, int columnsCount)
{//将宽度平均分成columnsCount份,返回每一份的起点
#if DEBUG
if (columnsCount == 0)
throw new ApplicationException("columnsCount参数不允许为0");
#endif
List<double> startXs = new List<double>();
var start = marginLeft;
startXs.Add(start);
double averageLength = (width - marginLeft) / columnsCount;
for (int i = 0; i < columnsCount - 1; i++) {
start += averageLength;
startXs.Add(start);
}
return startXs;
}
private List<double> GetColumnWidthsStartX(double marginLeft, double[] columnWidths, int columnCount, out List<double> columnWidthsAll)
{
columnWidthsAll = new List<double>();
List<double> widthsStartX = new List<double>();
widthsStartX.Add(marginLeft);
foreach (var c in columnWidths) {
marginLeft += c;
widthsStartX.Add(marginLeft);
columnWidthsAll.Add(c);
}
if (columnWidths.Length < columnCount) {
var width = PrinterUtil.GetWidth(_DefaultWidth_CharCount, FontSize, TypeFace);
for (int i = 0; i < columnCount - columnWidths.Length; i++) {
marginLeft += width;
widthsStartX.Add(marginLeft);
columnWidthsAll.Add(width);
}
}
return widthsStartX;
}
private void DrawDetail(double startY, DrawingContext dc, IList<string> values, List<double> columnWidthStartXs, List<double> columnWidthsAll, out double nextLineStartY)//前提条件:将values设置为与_ColumnNames对应起来
{
nextLineStartY = startY;
Point point = new Point(0, startY);
for (int i = 0; i < values.Count; i++) {
point.X = columnWidthStartXs.ElementAt(i);
PrinterUtil.DrawTextWithRectangle(point, dc, values[i], FontSize, TypeFace, columnWidthsAll[i], 0.5, 5, LineDistance, out nextLineStartY);
}
}
private void DrawSumDetail(double startY, DrawingContext dc, IList<string> values, List<double> columnWidthStartXs, List<double> columnWidthsAll, out double nextLineStartY)
{
nextLineStartY = startY;
Point point = new Point(0, startY);
bool isBlack = false;
for (int i = 0; i < values.Count; i++) {
if (i == 0 && string.IsNullOrEmpty(values[i]))
isBlack = true;
if (isBlack && string.IsNullOrEmpty(values[i]))
continue;
if (isBlack) {
isBlack = false;
double width = 0;
for (int j = 0; j < i; j++) {
width += columnWidthsAll[j];
}
double sumTextMagin = width + 5 - columnWidthsAll[i - 1];
point.X = columnWidthStartXs.ElementAt(0);
PrinterUtil.DrawTextWithRectangle(point, dc, "合计", FontSize, TypeFace, width, 0.5, sumTextMagin, LineDistance, out nextLineStartY);
}
point.X = columnWidthStartXs.ElementAt(i);
PrinterUtil.DrawTextWithRectangle(point, dc, values[i], FontSize, TypeFace, columnWidthsAll[i], 0.5, 5, LineDistance, out nextLineStartY);
}
}
private void DrawSumRow(double startY, DrawingContext dc, List<double> columnWidthStartXs, List<double> columnWidthsAll, out double nextLineStartY)
{
if (!AllowSum) {
nextLineStartY = startY;
return;
}
List<string> values = new List<string>();
foreach (var name in _ColumnNames) {
if (SumColumnNames.Contains(name)) {
values.Add(GetSumValue(_DetailRows, name));
} else
values.Add(string.Empty);
}
DrawSumDetail(startY, dc, values, columnWidthStartXs, columnWidthsAll, out nextLineStartY);
}
private string GetSumValue(List<Dictionary<string, string>> detailRows, string name)
{
decimal sum = 0;
foreach (var row in detailRows) {
if (!row.Keys.Contains(name))
return string.Empty;
if (string.IsNullOrEmpty(row[name]))
continue;
decimal value;
if (decimal.TryParse(row[name], out value))
sum += value;
else
throw new ApplicationException("计算合计数据出错");
}
//return sum.ToString("F2");
return NumToStringUtil.GetPointAtMost(sum);
}
private List<string> PrepareRow(Dictionary<string, string> row, string[] columnNames)
{
List<string> result = new List<string>();
foreach (var item in columnNames) {
if (row.Keys.Contains(item))
result.Add(row[item]);
else
result.Add(string.Empty);
}
return result;
}
/// <summary>
/// 绘制键值对信息
/// </summary>
/// <param name="infos">键值对信息</param>
/// <param name="charCount_Key">键的个数</param>
/// <param name="columnsCount">一行上,显示的键值对的个数</param>
/// <param name="startY">起点Y坐标</param>
/// <returns>下一行的Y坐标</returns>
private double DrawPairsInfo(DrawingContext dc, Dictionary<string, string> infos, double fontSize, int charCount_Key, int columnsCount, double startY)//返回下一行的Y坐标
{
double nextLineStartY = startY;
var charLength_Key = PrinterUtil.GetWidth(charCount_Key, fontSize, TypeFace);
var averageWidths_StartX = GetAverageWidthsStartX(PageWidth, MarginLeft, columnsCount);
Point point = new Point(averageWidths_StartX[0], nextLineStartY);
for (int i = 0; i < infos.Count; i++) {
var columnIndex = i % columnsCount;
point.X = averageWidths_StartX[columnIndex];
if (columnIndex == 0 && i != 0) {
point.Y = nextLineStartY;
}
PrinterUtil.DrawText(point, dc, infos.ElementAt(i).Key, fontSize, LineDistance, TypeFace);
PrinterUtil.DrawText(new Point(point.X + charLength_Key, point.Y), dc, infos.ElementAt(i).Value, fontSize, LineDistance, TypeFace, out nextLineStartY);
}
return nextLineStartY;
}
private double DrawInfos(DrawingContext dc, double fontSize, double startX, double startY, params string[] infos)
{
if (infos == null || infos.Length == 0)
return startY;
double next = startY;
Point point = new Point(startX, next);
foreach (var item in infos) {
PrinterUtil.DrawText(point, dc, item, fontSize, LineDistance, TypeFace, out next);
point.Y = next;
}
return next;
}
private double DrawFooter(DrawingContext dc, double startY, int pageIndex)
{
if (!ShowPrinter && !ShowPrintTime && !ShowPageNum)
return startY;
Dictionary<string, string> pairs = new Dictionary<string, string>();
int charCount_Key = 5;
int columnCount = 3;
if (ShowPrinter)
pairs.Add("打印人:", string.IsNullOrEmpty(Printer) ? string.Empty : Printer);
if (ShowPrintTime)
pairs.Add("打印时间:", DateTime.Now.ToString());
if (ShowPageNum)
pairs.Add("第" + (pageIndex + 1) + "页", "共" + PageNumUtil.GetTotalPageCount(_DetailRows.Count, _DetailMaxRows) + "页");
return DrawPairsInfo(dc, pairs, FontSize_Footer, charCount_Key, columnCount, startY);
}
}
}