C#窗体中PrintDocument打印时文本溢出无法自动换行如何解决
C# PrintDocument打印RichTextBox溢出文本自动换行实现方案
RichTextBox编辑状态下的自动换行是控件根据自身显示宽度做的内置适配,直接调用PrintDocument打印时,默认不会按打印页的可打印区域重新计算折行位置,就会出现超出区域的文本被截断、无法自动换行的问题。
推荐实现方案(支持保留富文本格式)
借助Win32 API的EM_FORMATRANGE消息,让RichTextBox直接根据指定的打印区域宽度自动完成折行计算、渲染输出,同时可以自动处理分页,兼容富文本的字体、颜色、内嵌元素格式。
- 首先引入所需的Win32 API和结构体定义
using System.Runtime.InteropServices; [StructLayout(LayoutKind.Sequential)] private struct RECT { public int Left; public int Top; public int Right; public int Bottom; } [StructLayout(LayoutKind.Sequential)] private struct CHARRANGE { public int cpMin; public int cpMax; } [StructLayout(LayoutKind.Sequential)] private struct FORMATRANGE { public IntPtr hdc; public IntPtr hdcTarget; public RECT rc; public RECT rcPage; public CHARRANGE chrg; } private const int WM_USER = 0x0400; private const int EM_FORMATRANGE = WM_USER + 57; [DllImport("user32.dll")] private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
- 在PrintDocument的对应事件中编写核心打印逻辑,代码中可直接指定打印边距,控件会自动按边距宽度折行
// 定义全局变量记录多页打印时的起始字符位置 private int _printStartChar = 0; private void printDocument1_BeginPrint(object sender, System.Drawing.Printing.PrintEventArgs e) { // 打印开始时重置起始位置 _printStartChar = 0; } private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e) { RichTextBox targetRtb = richTextBox1; // 将打印边距单位转换为RichTextBox渲染使用的缇单位 RECT printRect = new RECT() { Top = (int)Math.Round(e.MarginBounds.Top * 100 / e.Graphics.DpiY), Bottom = (int)Math.Round(e.MarginBounds.Bottom * 100 / e.Graphics.DpiY), Left = (int)Math.Round(e.MarginBounds.Left * 100 / e.Graphics.DpiX), Right = (int)Math.Round(e.MarginBounds.Right * 100 / e.Graphics.DpiX) }; IntPtr hdc = e.Graphics.GetHdc(); FORMATRANGE fmtParam = new FORMATRANGE() { hdc = hdc, hdcTarget = hdc, rc = printRect, rcPage = printRect, chrg = new CHARRANGE() { cpMin = _printStartChar, cpMax = -1 // -1代表处理到文本末尾 } }; IntPtr paramPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(fmtParam)); Marshal.StructureToPtr(fmtParam, paramPtr, false); // 发送渲染消息,返回值为当前页渲染完成后最后一个字符的索引 IntPtr result = SendMessage(targetRtb.Handle, EM_FORMATRANGE, (IntPtr)1, paramPtr); int lastCharOnPage = result.ToInt32(); // 释放非托管资源 Marshal.FreeCoTaskMem(paramPtr); e.Graphics.ReleaseHdc(hdc); SendMessage(targetRtb.Handle, EM_FORMATRANGE, IntPtr.Zero, IntPtr.Zero); // 判断是否需要分页 if (lastCharOnPage < targetRtb.TextLength) { e.HasMorePages = true; _printStartChar = lastCharOnPage; } else { e.HasMorePages = false; } }
简易纯文本方案
如果不需要保留富文本格式,也可以直接用GDI+的测量方法手动折行:
- 调用
e.Graphics.MeasureString逐段测量文本在打印区域宽度下的可容纳字符数 - 拆分文本为逐行内容,调用
e.Graphics.DrawString逐行输出,直到填满当前页后触发分页
- 注意:该方案会丢失RichTextBox内的字体样式、颜色、图片等富文本格式,仅适合纯文本打印场景。

内容的提问来源于stack exchange,提问作者Manly
相关产品推荐
相关产品推荐

