打印设置自定义表尾_分享一个自定义打印套打方案

本文介绍了一个项目中实现的单据套打功能,包括灵活的打印模板设置、自定义打印项、常规打印功能(如小计/合计)以及实时预览。通过分析关键角色对象,如打印项、打印模板数据、表头表尾和打印控制器,阐述了功能的实现思路,并详细讲解了可拖动容器控件的实现,以实现模板的动态调整。
摘要由CSDN通过智能技术生成

最近项目中需要实现单据套打的功能,实现后,留此日记以备忘,同时为有类似需求的同学提供一种解决方案.

原始需求:

1. 打印模板支持灵活自定义,支持可视化设置(含设置打印项是否打印,及位置,大小,字体等…).

2. 支持自定义打印项.

3. 常规的打印项设置(如自动打印小计/合计,表头表尾等…)

4. 在设置模板的时候,支持实时预览.

首先,先上个图, 给大家看一下打印套打模板的大致效果图,各位同学看着顺眼再往下看.

e683d1d8829cdde351ad0ef637296cb7.png

接下来,我们简单的来讨论一下如何实现这个需求,简单分析一下,在这个功能中,可能需要存在以下角色对象.

1. 打印项: 姑且暂称为 PrintItem ,我们不难发现至少应该具备以下属性: 标签名称, 数据源, 宽度, 高度, 字体, 颜色, 对齐方式, 打印坐标(表明细区域可能为打印位置索引)…

2. 打印模板数据: 姑且暂称为 BillPrintData ,为了简单起见,我们可以将这个打印模板数据拆分为 表头, 表体, 表尾,因为表头和表尾在控制上行为基本上是一致的,由此我们很自然的衍生出来了 表头表尾 以及 中间的”表体 ”这两个对象.

3. 表头表尾: 姑且暂称为 BillHeaderOrBottom ,不难看出,其理所应当的应该包含一个打印项集合,一个为这些打印项赋值的数据源. 以及这个区域的大小和其实坐标位置.

4. 表体即中间表格部分,姑且暂称为 BillGridBody, 具有和 BillHeaderOrBottom 一样的内容,同时还应该有设置那些行需要小计/合计.

5. 打印控制器,姑且暂称为  BillPrinter , 主要功能为加载打印格式,同时根据参数配置,控制输出到打印机.

有了上面的简单分析,我们来简单画一下这个需求的类结构图.

d68b0dd01456b66e45e638b4c9ea8c3b.png

有了上面这些分析,下面我们一起来看下如何来实现这个功能.我们先来实现一个可运行状态随意拖动的容器控件.

为了简单起见,这里我们不妨就暂称我们将要扩展的Panel容器名称为 MoveControlPanel,该容器至少需要实现以下功能

1. 识别当前鼠标位置,是否处于某个内部组件的边框位置,以让鼠标显示出对应的图标(拉伸,移动…)

2.移动鼠标以改变内部某个组件的大小及坐标,

3.保存容器内每个组件的当前坐标及大小.

为了便于描述当前光标状态,我们不妨定义一个枚举.姑且称其为 EMousePointPosition, 其至少应该具有如下定义.

///

/// 光标状态

///

private enumEMousePointPosition

{

///

/// 无

///

None = 0,

///

/// 处于拉伸右边框位置

///

MouseSizeRight = 1,

///

/// 处于拉伸左边框位置

///

MouseSizeLeft = 2,

///

/// 处于拉伸下边框位置

///

MouseSizeBottom = 3,

///

/// 处于拉伸上边框位置

///

MouseSizeTop = 4,

///

/// 处于拉伸左上角位置

///

MouseSizeTopLeft = 5,

///

/// 处于拉伸右上角位置

///

MouseSizeTopRight = 6,

///

/// 处于拉伸左小角位置

///

MouseSizeBottomLeft = 7,

///

/// 处于拉伸右下角位置

///

MouseSizeBottomRight = 8,

///

/// 鼠标拖动状态

///

MouseDrag = 9}

接下来,我们需要通过处理容器内控件的 鼠标按下/鼠标移动/鼠标离开 三个事件,来实现对容器内各个控件的大小/位置的改变.

///

/// 初始化鼠标事件

///

public voidInitMouseEvent()

{

for (int i = 0; i < this.Controls.Count; i++)

{

this.Controls[i].MouseDown += newSystem.Windows.Forms.MouseEventHandler(ctrl_MouseDown);

this.Controls[i].MouseLeave += newSystem.EventHandler(ctrl_MouseLeave);

this.Controls[i].MouseMove += newSystem.Windows.Forms.MouseEventHandler(ctrl_MouseMove);

}

}

接下来我们去尝试实现以下鼠标移动事件,

1) 如果当前没有按下鼠标按键,则去尝试获取当前鼠标的状态,并设置当前光标样式

2) 当按下了鼠标左键,此时需要根据光标状态来改变当前控件的坐标/大小.

private const int Band = 5; //边框大小

private const int MinWidth = 10; //最小宽度

private const int MinHeight = 10; //最小高度

private EMousePointPosition _currentMousePointPosition; //当前鼠标状态

private Point p, p1; //临时变量,缓存改变前后鼠标坐标

///

/// 获取鼠标坐标状态

///

/// 当前控件大小

/// 当前鼠标坐标位置

///

privateEMousePointPosition GetMousePointPosition(Size size, Point e)

{

if ((e.X >= -1 * Band) | (e.X <= size.Width) | (e.Y >= -1 * Band) | (e.Y <=size.Height))

{

if (e.X

{

if (e.Y

{

returnEMousePointPosition.MouseSizeTopLeft;

}

else{

if (e.Y > -1 * Band +size.Height)

{

returnEMousePointPosition.MouseSizeBottomLeft;

}

else{

returnEMousePointPosition.MouseSizeLeft;

}

}

}

else{

if (e.X > -1 * Band +size.Width)

{

if (e.Y

{

returnEMousePointPosition.MouseSizeTopRight;

}

else{

if (e.Y > -1 * Band +size.Height)

{

returnEMousePointPosition.MouseSizeBottomRight;

}

else{

returnEMousePointPosition.MouseSizeRight;

}

}

}

else{

if (e.Y

{

returnEMousePointPosition.MouseSizeTop;

}

else{

if (e.Y > -1 * Band +size.Height)

{

returnEMousePointPosition.MouseSizeBottom;

}

else{

returnEMousePointPosition.MouseDrag;

}

}

}

}

}

else{

returnEMousePointPosition.None;

}

}

///

/// 设置光标状态

///

/// 当前引发的鼠标事件参数

/// 当前控件大小

private voidSetCursorStyle(MouseEventArgs e, Control tempCtrl)

{

_currentMousePointPosition = GetMousePointPosition(tempCtrl.Size, e.Location); //判断光标的位置状态

//根据光标位置状态设置当前鼠标状态

switch(_currentMousePointPosition)

{

caseEMousePointPosition.None:

this.Cursor =Cursors.Arrow;

break;

case EMousePointPosition.MouseDrag: //移动效果

this.Cursor =Cursors.SizeAll;

break;

case EMousePointPosition.MouseSizeBottom: //上下边框

caseEMousePointPosition.MouseSizeTop:

this.Cursor =Cursors.SizeNS;

break;

case EMousePointPosition.MouseSizeLeft: //左右边框

caseEMousePointPosition.MouseSizeRight:

this.Cursor =Cursors.SizeWE;

break;

case EMousePointPosition.MouseSizeBottomLeft: //左上/右下

caseEMousePointPosition.MouseSizeTopRight:

this.Cursor =Cursors.SizeNESW;

break;

case EMousePointPosition.MouseSizeBottomRight: //右下左上

caseEMousePointPosition.MouseSizeTopLeft:

this.Cursor =Cursors.SizeNWSE;

break;

default:

break;

}

}

///

/// 处理鼠标移动事件

///

///

///

private void ctrl_MouseMove(objectsender, MouseEventArgs e)

{

Control tempCtrl = sender asControl;

if (e.Button ==MouseButtons.Left)

{

switch(_currentMousePointPosition)

{

caseEMousePointPosition.MouseDrag:

tempCtrl.Left = tempCtrl.Left + e.X -p.X;

tempCtrl.Top = tempCtrl.Top + e.Y -p.Y;

break;

caseEMousePointPosition.MouseSizeBottom:

tempCtrl.Height = tempCtrl.Height + e.Y -p1.Y;

p1.X =e.X;

p1.Y = e.Y; //记录光标拖动的当前点

break;

caseEMousePointPosition.MouseSizeBottomRight:

tempCtrl.Width = tempCtrl.Width + e.X -p1.X;

tempCtrl.Height = tempCtrl.Height + e.Y -p1.Y;

p1.X =e.X;

p1.Y = e.Y; //记录光标拖动的当前点

break;

caseEMousePointPosition.MouseSizeRight:

tempCtrl.Width = tempCtrl.Width + e.X -p1.X;

// lCtrl.Height = lCtrl.Height + e.Y - p1.Y;

p1.X =e.X;

p1.Y = e.Y; //'记录光标拖动的当前点

break;

caseEMousePointPosition.MouseSizeTop:

tempCtrl.Top = tempCtrl.Top + (e.Y -p.Y);

tempCtrl.Height = tempCtrl.Height - (e.Y -p.Y);

break;

caseEMousePointPosition.MouseSizeLeft:

tempCtrl.Left = tempCtrl.Left + e.X -p.X;

tempCtrl.Width = tempCtrl.Width - (e.X -p.X);

break;

caseEMousePointPosition.MouseSizeBottomLeft:

tempCtrl.Left = tempCtrl.Left + e.X -p.X;

tempCtrl.Width = tempCtrl.Width - (e.X -p.X);

tempCtrl.Height = tempCtrl.Height + e.Y -p1.Y;

p1.X =e.X;

p1.Y = e.Y; //记录光标拖动的当前点

break;

caseEMousePointPosition.MouseSizeTopRight:

tempCtrl.Top = tempCtrl.Top + (e.Y -p.Y);

tempCtrl.Width = tempCtrl.Width + (e.X -p1.X);

tempCtrl.Height = tempCtrl.Height - (e.Y -p.Y);

p1.X =e.X;

p1.Y = e.Y; //记录光标拖动的当前点

break;

caseEMousePointPosition.MouseSizeTopLeft:

tempCtrl.Left = tempCtrl.Left + e.X -p.X;

tempCtrl.Top = tempCtrl.Top + (e.Y -p.Y);

tempCtrl.Width = tempCtrl.Width - (e.X -p.X);

tempCtrl.Height = tempCtrl.Height - (e.Y -p.Y);

break;

default:

break;

}

if (tempCtrl.Width < MinWidth) tempCtrl.Width =MinWidth;

if (tempCtrl.Height < MinHeight) tempCtrl.Height =MinHeight;

}

else{

SetCursorStyle(e, tempCtrl);

}

}

接下来,我们在处理一下鼠标按下及鼠标离开事件,

1) 鼠标按下,记录当前坐标

2) 鼠标离开,还原光标状态

///

/// 处理鼠标按下事件

///

///

///

private void ctrl_MouseDown(objectsender, System.Windows.Forms.MouseEventArgs e)

{

if (e.Button ==System.Windows.Forms.MouseButtons.Left)

{

p.X =e.X;

p.Y =e.Y;

p1.X =e.X;

p1.Y =e.Y;

}

}

///

/// 处理鼠标离开

///

///

///

private void ctrl_MouseLeave(objectsender, EventArgs e)

{

_currentMousePointPosition =EMousePointPosition.None;

this.Cursor =Cursors.Arrow;

}

ok, 截止到这里,这个控件基本上就已完成了,已实现在界面中实时拖拽,改变大小功能.

此功能简略的讲解到这里,我想若有类似需求的朋友已经能知道后面的路怎么走了.剩下无非就是在一个设计器界面中引入这个控件,然后根据用户拖动的位置将坐标记录下来,在打印的时候,将数据源这个格式一起绑定后交给打印控制处理器负责打印即可.

一、应用场景与出发点 同一个系统中,为了解决不同的客户可能需要设计不同的单据打印模板,实现此方法可能是: 1、设计不同的自带RDLC报表文件,根据当前客户加载不同的报表并打印 2、GDI+绘图 和 打印组件 ,不同的客户创建不同的绘图XML格式的模板内容 3、其他第三方组件 主要对比一下前两种方法,第一种方法不好之处在于,不灵活,开发者必须地每个客户制订一个报表,不推荐采用。第二种方法,修改对应的模板内容就可以了, 模板内容可以是Xml文件,也可以是存放在数据库中的Xml格式字符串。推荐采用这种方法。然后这种方法的也有一个棘手问题 :如何让用户快速、方便地设计打印模板,本示例就是为了解决这个问题。 二、实现思路与原理 功能概要:设计一个界面,支持用户自由添加 要打印的项,文本,直线,图片 等,并且可以方便改变打印项的 字体、颜色、粗细、位置,设计时支持效果预览。 技术要点:GDI+绘图、拖动控件、XML解析、自定义控件 三、相关类介绍 绘图工具类:DrawHelper 实现 xml格式模板 与 打印项 之间进行互相转换,在目标画板中绘制 拖动工具类:WinHelper 实现控件的鼠标拖动,键盘移动 自定义控件:用于显示文字的文本框 TextBoxExt、用于显示直线的标签 LabelExt 主窗体代码:用于用户操作,添加,删除,编辑,打印项 详细介绍请参照我的博文:http://de.cel.blog.163.com/blog/static/51451236201472215450939/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值