Devexpress打印预览(PrintPreview)时修改打印数据

引言

打印功能对于一个系统来说通常是强需求,每个系统都会根据自己所提供的服务生成不同的打印模板,但光有模板往往不够用,因为有时会需要在打印预览(PrintPreview)时根据打印内容临时修改打印的数据,而且这种修改并不需要应用于打印数据源的修改,只需要打印出变更后的内容就行了;所以我们只需修改内存中的数据,然后重新打印就可以了。

在最新的Devexpress 17.1中XRLableXRTableCellCharacter CombEditOptions属性,可以实现直接在打印预览界面(PrintPreview)上修改打印的数据,具体请参考官方示例;但很可惜我相信大部分的系统都还和我用的一样,没有使用17.1,所以只能乖乖修改数据然后重新打印。

效果图

打印预览界面双击修改打印数据

步骤

  • 首先我们设计好打印内容的模板

打印内容模板:

打印内容模板

​ 打印预览效果图:

打印预览效果图

打印预览(PrintPreview)中的每一个Page表示一个model,我们需要一种方式来修改对应model的数据;通过查看官方示例我们知道,打印预览界面(PrintPreview)的元素实际上都是Brick类型的子类,你点击每一个Page都触发的是BrickClick事件:

通过调试可以发现打印预览界面(PrintPreview)元素构成的层级关系:

层级关系

所以我们可以把每一个model的主键保存在每一个Page中,我选用了BrickTable(你也可以选择其它元素,只是在我的例子中刚好每一个Page对应于一个Table)

  • 所以第二步是保存model的主键到BrickTable的AnchorName中(如果你在打印预览界面(PrintPreview)使用了目录功能,可能就需要用其它方式保存数据了)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/// <summary>
/// 保存的打印页主键列表
/// </summary>
private List<int> ListStudentId { get;set; }
/// <summary>
/// 生成Table明细数据之后的事件处理方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Detail1_AfterPrint(object sender, EventArgs e)
{
var dtRowView = DetailReport.GetCurrentRow() as DataRowView;
if (dtRowView != null)
{
var studentId =Convert.ToInt32(dtRowView["StudentId"]);

ListStudentId.Add(studentId);//将Key记录下来
}
}
  • 第三步,在生成打印明细数据时将主键赋值到BrickTable的AnchorName上
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/// <summary>
/// 将生成打印明细的数据打印到界面上的事件处理方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tbStudentDetail_PrintOnPage(object sender, PrintOnPageEventArgs e)
{
if (ListStudentId.Any())
{
//将主键保存到生成的打印对象的ID中
var iterator = new DevExpress.XtraPrinting.Native.NestedBrickIterator(Pages[e.PageIndex].InnerBricks);
while (iterator.MoveNext())
{
var visualBrick = iterator.CurrentBrick as VisualBrick;
if (visualBrick != null)
//PutStateToBrick(visualBrick, PrintingSystem);
{
if (visualBrick.BrickType == "Table")
//if (string.IsNullOrEmpty(visualBrick.Text) && string.IsNullOrEmpty(visualBrick.ID))
{
if (ListStudentId.Any())
{
visualBrick.ID = "StudentId";
//visualBrick.Text = listKey[visualBrick.ID];
visualBrick.AnchorName = ListStudentId[0].ToString();
ListStudentId.RemoveAt(0);
}
else
{
break;
}
}
}
//Do some actions based on your requirements
}
//}
//ListStudentId.RemoveAt(0);
}
}
  • 剩下的就是在打印预览界面(PrintPreview)双击Page弹出修改属性窗体后重新打印数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/// <summary>
/// 双击打印预览页面的事件处理方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void documentViewer1_BrickDoubleClick(object sender, DevExpress.XtraPrinting.Control.BrickEventArgs e)
{
var studentId = string.Empty;
var iterator = new DevExpress.XtraPrinting.Native.NestedBrickIterator(e.Page.InnerBricks);
while (iterator.MoveNext())
{
VisualBrick visualBrick = null;
try
{
visualBrick = iterator.CurrentBrick as VisualBrick;
}
catch (ArgumentOutOfRangeException)
{
//对弹出的窗口点击确认后会报此异常,因为超出了report的范围
//对这种异常不做处理
}
if (visualBrick != null)
//PutStateToBrick(visualBrick, PrintingSystem);
//读取打印模板界面保存的report主键数据
{
if (visualBrick.ID == "StudentId")
{
studentId = visualBrick.AnchorName;
}
}

//Do some actions based on your requirements
}

if(string.IsNullOrEmpty(studentId))return;
else
{
//MessageBox.Show($"点击的学生主键为{studentId}");
var newForm = new EditProperties(ListReport.First().PModel.ListStudents
.SingleOrDefault(c => c.StudentId == Convert.ToInt32(studentId)));
var dlg= newForm.ShowDialog();
if (dlg == DialogResult.OK)
{
//重新打印
var newReport=ListReport.First();
newReport.SetDataSource();
LoadData();
}
}
}

其中,修改属性的窗体使用了类似设计器属性修改界面的PropertyGridControl控件,当然你也可以自定义界面来实现类似功能,只要可以修改属性的值就行了

文章描述内容的示例项目已放在了Github上。


Devexpress打印预览(PrintPreview)时修改打印数据
http://blog.wangshuai.app/2017-10-7-Devexpress打印预览(PrintPreview)时修改打印数据/
作者
王帅
发布于
2017年10月7日
许可协议