C#中使用GDI+与PrintDocument实现打印
近日做报表,需要实现一个比较特殊的打印:针对不同患者的药费记录,打印不同的清单,并且支持一次打印多个患者的记录。其效果看起来应该像下面这个样子:
如上所述,使用弱智的水晶报表,就会遇到如何构造数据源的问题,由于不同患者的药费记录和遗嘱记录都不同,而且整体上需要一个患者一个清单,所以其数据源只能为一个数组,而不是简单的DataTable。小弟一向对大且笨拙的CrystalReport不感冒,再加上对GDI+比较痴迷,索性费点功夫,使用GDI+来打印上述报表。
首先需要构造数据源,创建一个ArrayList对象a,然后再把每个患者需要打印的东东保存为一个ArrayList放入ATotal中。
1
if (a != null)
2
{
3
a.Clear();
4
}
5
for (int m = 0; m < 3; m++)
6
{
7
ArrayList a1 = new ArrayList();
8
string x0 = "住院患者一日清单";
9
string x1 = "入院日期:2007-11-11 结算日期:2007-11-11至 2007-11-15 ";
10
string x2 = "姓名:欧阳峰 住院号:20090909090 床:512 妇科";
11
string x3 = "项目编号*项目名称*单价*数量*单位"; // 注意其中的*标志
12
string[] x4 = newstring[90]; // 假设有90条数据,不固定
13
for (int i = 0; i < 90; i++)
14
{
15
x4[i] = "juejue1984*www.loyosoft.com*11.00*11*鸟"; // 注意其中的*标志
16
}
17
string x5 = "费用累计:2500*小计:400 ";
18
string x6 = "累计按金:3600*余额:1600 ";
19
string x7 = "";
20
string x8 = "";
21
a1.Add(x0);
22
a1.Add(x1);
23
a1.Add(x2);
24
a1.Add(x3);
25
a1.Add(x4);
26
a1.Add(x5);
27
a1.Add(x6);
28
a1.Add(x7);
29
a1.Add(x8);
30
a.Add(a1);
31
}

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

然后在PrintDocument对象的PrinPage事件作如下处理:
1
Size sTotal = new Size(420, e.PageBounds.Height);
2
Graphics g = e.Graphics;
3
Font fTitle = new Font("宋体", 16); // 标题字体
4
int titleHeight = 20; // 标题的高度
5
int textHeight = 13; // 普通文本的高度
6
Font fText = new Font("宋体", 9); // 文本字体
7
8
int top = 30; // 顶部间距
9
int left = 10; // 左边距
10
int right = 10; // 右边距
11
int titleMargin = 15; // 标题和正文行间距
12
int textMargin = 5; // 行间距
13
int rectangleMargin = 3; // 边框和文本间距
14
int oneHeight = 19; // 一行文本 + 2 * 行间距的高度
15
int oneWidth = sTotal.Width - left - right; // 打印内容的宽度
16
17
int y = 0;
18
while (j < a.Count) // Count为要打印的患者个数
19
{
20
ArrayList a3 = a[j] as ArrayList; // a3位要打印的患者的清单内容。
21
if (y >= e.PageBounds.Height - titleHeight) // 如果已经到了底部则开始下一页
22
{
23
e.HasMorePages = true;
24
return;
25
}
26
//
27
string x = a3[0] as string;
28
if (!this.JudgeString(x)) // 如果此行记录还未打印过
29
{
30
31
//string x = "住院患者一日清单"; // a1
32
SizeF sTitle = g.MeasureString(x, fTitle); // 计算标题的长度和高度
33
Point pTitle = new Point((sTotal.Width - (int)sTitle.Width) / 2, y + top); // 使标题绝对居中
34
y += top; // 1
35
//
36
g.DrawString(x, fTitle, Brushes.Black, pTitle.X, y); // 标题
37
this.AddPrintedSign(ref x);
38
a3[0] = x; // 标记该患者的此行内容为已经打印过
39
y += titleHeight + titleMargin; // Y坐标下移
40
}
41
42
43
//
44
if (y >= e.PageBounds.Height - oneHeight)
45
{
46
e.HasMorePages = true;
47
return;
48
}
49
50
//// 正文第一行
51
string r1 = a3[1] as String;
52
if (!this.JudgeString(r1))
53
{
54
g.DrawString(r1, fText, Brushes.Black, left, y);
55
this.AddPrintedSign(ref r1);
56
a3[1] = r1;
57
y += textHeight + textMargin; // 3
58
}
59
60
//
61
if (y >= e.PageBounds.Height - oneHeight)
62
{
63
e.HasMorePages = true;
64
return;
65
}
66
//
67
68
//// 正文第二行
69
string r2 = a3[2] as String;
70
if (!this.JudgeString(r2))
71
{
72
g.DrawString(r2, fText, Brushes.Black, left, y);
73
this.AddPrintedSign(ref r2);
74
a3[2] = r2;
75
y += textHeight + textMargin; // 4
76
}
77
78
//
79
if (y >= e.PageBounds.Height - oneHeight)
80
{
81
e.HasMorePages = true;
82
return;
83
}
84
//
85
86
////内容
87
string column = a3[3] as string;
88
if (!this.JudgeString(column))
89
{
90
string[] c = column.Split(new char[] { '*' });
91
Rectangle R1 = new Rectangle(left, y, oneWidth, oneHeight);
92
g.DrawRectangle(Pens.Black, R1);
93
g.DrawLine(Pens.Black, new Point(R1.X + R1.Width * 4 / 16, R1.Y), new Point(R1.X + R1.Width * 4 / 16, R1.Y + R1.Height));
94
g.DrawLine(Pens.Black, new Point(R1.X + R1.Width * 10 / 16, R1.Y), new Point(R1.X + R1.Width * 10 / 16, R1.Y + R1.Height));
95
g.DrawLine(Pens.Black, new Point(R1.X + R1.Width * 12 / 16, R1.Y), new Point(R1.X + R1.Width * 12 / 16, R1.Y + R1.Height));
96
g.DrawLine(Pens.Black, new Point(R1.X + R1.Width * 14 / 16, R1.Y), new Point(R1.X + R1.Width * 14 / 16, R1.Y + R1.Height));
97
98
g.DrawString(c[0], fText, Brushes.Black, R1.X + rectangleMargin, R1.Y + rectangleMargin); // 项目编号 //a4.1
99
g.DrawString(c[1], fText, Brushes.Black, R1.X + rectangleMargin + R1.Width * 4 / 16, R1.Y + rectangleMargin);// 项目名称 // a4.2
100
g.DrawString(c[2], fText, Brushes.Black, R1.X + rectangleMargin + R1.Width * 10 / 16, R1.Y + rectangleMargin);//单价 //a4.3
101
g.DrawString(c[3], fText, Brushes.Black, R1.X + rectangleMargin + R1.Width * 12 / 16, R1.Y + rectangleMargin); //数量 //a4.4
102
g.DrawString(c[4], fText, Brushes.Black, R1.X + rectangleMargin + R1.Width * 14 / 16, R1.Y + rectangleMargin);//单位 //a4.5
103
104
105
this.AddPrintedSign(ref column);
106
a3[3] = column;
107
y += oneHeight;
108
}
109
//
110
if (y >= e.PageBounds.Height - oneHeight)
111
{
112
e.HasMorePages = true;
113
return;
114
}
115
//
116
117
string[] row = a3[4] as string[];
118
int Row = row.Length;
119
for (int i = 0; i < Row; i++)
120
{
121
if (!this.JudgeString(row[i]))
122
{
123
string[] r = row[i].Split(new Char[] { '*' });
124
g.DrawLine(Pens.Black, new Point(left + oneWidth * 0 / 16, y), new Point(left + oneWidth * 0 / 16, y + oneHeight));
125
g.DrawLine(Pens.Black, new Point(left + oneWidth * 4 / 16, y), new Point(left + oneWidth * 4 / 16, y + oneHeight));
126
g.DrawLine(Pens.Black, new Point(left + oneWidth * 10 / 16, y), new Point(left + oneWidth * 10 / 16, y + oneHeight));
127
g.DrawLine(Pens.Black, new Point(left + oneWidth * 12 / 16, y), new Point(left + oneWidth * 12 / 16, y + oneHeight));
128
g.DrawLine(Pens.Black, new Point(left + oneWidth * 14 / 16, y), new Point(left + oneWidth * 14 / 16, y + oneHeight));
129
g.DrawLine(Pens.Black, new Point(left + oneWidth * 16 / 16, y), new Point(left + oneWidth * 16 / 16, y + oneHeight));
130
131
g.DrawString(r[0], fText, Brushes.Red, left + rectangleMargin, y + rectangleMargin); //a5.1
132
g.DrawString(r[1], fText, Brushes.Red, left + rectangleMargin + oneWidth * 4 / 16, y + rectangleMargin); //a5.2
133
g.DrawString(r[2], fText, Brushes.Red, left + rectangleMargin + oneWidth * 10 / 16, y + rectangleMargin);//a5.3
134
g.DrawString(r[3], fText, Brushes.Red, left + rectangleMargin + oneWidth * 12 / 16, y + rectangleMargin);//a5.4
135
g.DrawString(r[4], fText, Brushes.Red, left + rectangleMargin + oneWidth * 14 / 16, y + rectangleMargin);//a5.5
136
137
this.AddPrintedSign(ref row[i]);
138
y += oneHeight;
139
}
140
//
141
if (y >= e.PageBounds.Height - oneHeight)
142
{
143
e.HasMorePages = true;
144
return;
145
}
146
//
147
148
if (i == Row - 1)
149
{
150
g.DrawLine(Pens.Black, new Point(left, y), new Point(sTotal.Width - left - right, y));
151
}
152
}
153
154
//费用累计
155
//
156
if (y >= e.PageBounds.Height - oneHeight)
157
{
158
e.HasMorePages = true;
159
return;
160
}
161
//
162
163
////费用累计
164
string feeTotal = a3[5] as String;
165
if (!this.JudgeString(feeTotal))
166
{
167
string[] feeT = feeTotal.Split(new char[] { '*' });
168
Rectangle R3 = new Rectangle(left, y, oneWidth, oneHeight);
169
g.DrawRectangle(Pens.Black, R3);
170
g.DrawLine(Pens.Black, new Point(left + oneWidth * 10 / 16, y), new Point(left + oneWidth * 10 / 16, y + R3.Height));
171
g.DrawString(feeT[0], fText, Brushes.Black, R3.X + rectangleMargin, y + rectangleMargin);
172
g.DrawString(feeT[1], fText, Brushes.Black, left + 2 * rectangleMargin + R3.Width * 10 / 16, y + rectangleMargin);
173
this.AddPrintedSign(ref feeTotal);
174
a3[5] = feeTotal;
175
y += oneHeight;
176
}
177
178
//
179
if (y >= e.PageBounds.Height - oneHeight)
180
{
181
e.HasMorePages = true;
182
return;
183
}
184
//
185
186
////费用按金
187
string feeBalance = a3[6] as String;
188
if (!this.JudgeString(feeBalance))
189
{
190
string[] feeB = feeBalance.Split(new char[] { '*' });
191
Rectangle R4 = new Rectangle(left, y, oneWidth, oneHeight);
192
g.DrawRectangle(Pens.Black, R4);
193
g.DrawLine(Pens.Black, new Point(left + oneWidth * 10 / 16, y), new Point(left + oneWidth * 10 / 16, y + R4.Height));
194
g.DrawString(feeB[0], fText, Brushes.Black, left + rectangleMargin, y + rectangleMargin);
195
g.DrawString(feeB[1], fText, Brushes.Black, left + 2 * rectangleMargin + oneWidth * 10 / 16, y + rectangleMargin);
196
197
this.AddPrintedSign(ref feeBalance);
198
a3[6] = feeBalance;
199
y += oneHeight;
200
}
201
202
203
//// 备注
204
//string remark = " 备注:此清单一式两份,正联由患者或者家属签字,科室留存,付联由患者保存,可随时查询。如24小时不签字,不质疑视为认可费用。如病情需要变更医嘱另行通知。\r\n" +
205
// " 注:省保或者市保的比例为自费比例。";
206
string remark1 = a3[7] as String;
207
208
SizeF remarkSize = g.MeasureString(remark1, fText);
209
int Row2 = (int)remarkSize.Width / (oneWidth - 2 * rectangleMargin);
210
if (Row2 * (oneWidth - 2 * rectangleMargin) < (int)remarkSize.Width)
211
{
212
Row2 += 1;
213
}
214
//
215
if (y >= e.PageBounds.Height - (Row2 + 1) * textHeight + 2 * rectangleMargin + Row2 * textMargin)
216
{
217
e.HasMorePages = true;
218
return;
219
}
220
//
221
if (!this.JudgeString(remark1))
222
{
223
Rectangle R5 = new Rectangle(left, y, oneWidth, (Row2 + 1) * textHeight + 2 * rectangleMargin + Row2 * textMargin);
224
g.DrawRectangle(Pens.Black, R5);
225
R5.Offset(2, 5);
226
g.DrawString(remark1, fText, Brushes.Black, R5);
227
this.AddPrintedSign(ref remark1);
228
a3[7] = remark1;
229
y += R5.Height;
230
}
231
232
//
233
if (y >= e.PageBounds.Height - 3 * oneHeight)
234
{
235
e.HasMorePages = true;
236
return;
237
}
238
239
////签字
240
string signature = a3[8] as String;
241
Rectangle R6 = new Rectangle(left, y, oneWidth, 2 * rectangleMargin + 3 * textHeight + 2 * textMargin);
242
if (!this.JudgeString(signature))
243
{
244
g.DrawRectangle(Pens.Black, R6);
245
g.DrawString(signature, fText, Brushes.Black, left, y + rectangleMargin + textMargin + textHeight);
246
this.AddPrintedSign(ref signature);
247
a3[8] = signature;
248
y += R6.Height;
249
}
250
j++;
251
}
252
j = 0; // 打印完毕
253
e.HasMorePages = false;

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

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

判断一行内容是否已经打印过:
1
/// <summary>
2
/// 判断该字符串最后一个字符是否是 * 号,如果是,则表示已经打印过
3
/// </summary>
4
/// <param name="s">需要判断的字符串</param>
5
/// <returns>如果有则返回true,否则返回false</returns>
6
private bool JudgeString( string s )
7
{
8
int l = s.Length;
9
if( s[ l - 1 ] != '*' )
10
{
11
return false;
12
}
13
return true;
14
}
15
16
将已经打印过的内容标记为已打印
17
/// <summary>
18
/// 将已经打印过的行最后一个字符标记为*,表示该行已经打印
19
/// </summary>
20
/// <param name="s">字符串对象</param>
21
private void AddPrintedSign( ref string s )
22
{
23
int l = s.Length;
24
s = s.Replace( s.Substring( l - 1, 1 ), "*" );
25
}
26

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

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端