You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# WinForm中如何实现RichTextBox内容(含格式、图片)的打印预览?

问题根源

你现有的代码仅调用DrawString绘制RichTextBox的纯文本内容,会丢失所有富文本格式(自定义字体、颜色、行间距、段落样式)和嵌入图片,也没有实现分页逻辑,完全无法满足需求。

实现思路

Windows系统原生的RichEdit控件(WinForm RichTextBox的底层控件)提供了EM_FORMATRANGE消息,支持将富文本内容完整渲染到指定的设备上下文(打印用的Graphics对象),可以1:1保留所有格式、图片,还能自动计算分页位置。我们只需要调用这个消息即可实现符合预期的打印/预览效果。

完整实现步骤

首先确保你的代码顶部已经引入了必要的命名空间:

using System.Drawing.Printing;
using System.Runtime.InteropServices;
  1. 在窗体类中添加需要的Win32互操作定义、全局打印状态变量
// 定义EM_FORMATRANGE消息常量
private const int WM_USER = 0x0400;
private const int EM_FORMATRANGE = WM_USER + 57;

// 定义相关结构体
[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;
}

// 导入SendMessage方法
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, ref FORMATRANGE lParam);

// 记录当前打印到的字符位置,用于分页
private int _printPosition;
  1. 处理PrintDocument的BeginPrint事件,初始化打印起始位置
private void printDocument1_BeginPrint(object sender, System.Drawing.Printing.PrintEventArgs e)
{
    // 打印开始时从第一个字符开始打印
    _printPosition = 0;
}
  1. 重写PrintPage事件的逻辑,用EM_FORMATRANGE渲染内容
private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
    FORMATRANGE fmtRange = new FORMATRANGE();
    RECT rect = new RECT();
    RECT pageRect = new RECT();

    // 转换打印边距为RichEdit要求的百分之一英寸单位
    rect.Left = (int)e.MarginBounds.Left * 1440 / e.Graphics.DpiX;
    rect.Top = (int)e.MarginBounds.Top * 1440 / e.Graphics.DpiY;
    rect.Right = (int)e.MarginBounds.Right * 1440 / e.Graphics.DpiX;
    rect.Bottom = (int)e.MarginBounds.Bottom * 1440 / e.Graphics.DpiY;

    pageRect.Left = (int)e.PageBounds.Left * 1440 / e.Graphics.DpiX;
    pageRect.Top = (int)e.PageBounds.Top * 1440 / e.Graphics.DpiY;
    pageRect.Right = (int)e.PageBounds.Right * 1440 / e.Graphics.DpiX;
    pageRect.Bottom = (int)e.PageBounds.Bottom * 1440 / e.Graphics.DpiY;

    fmtRange.chrg.cpMin = _printPosition;
    fmtRange.chrg.cpMax = -1; // 打印到内容末尾
    fmtRange.hdc = e.Graphics.GetHdc();
    fmtRange.hdcTarget = e.Graphics.GetHdc();
    fmtRange.rc = rect;
    fmtRange.rcPage = pageRect;

    // 发送消息渲染内容
    IntPtr res = SendMessage(richTextBox1.Handle, EM_FORMATRANGE, (IntPtr)1, ref fmtRange);

    // 释放设备上下文
    e.Graphics.ReleaseHdc(fmtRange.hdc);

    // 记录下一页的起始字符位置
    _printPosition = res.ToInt32();

    // 判断是否还有剩余内容需要打印
    e.HasMorePages = _printPosition < richTextBox1.TextLength;

    // 释放缓存
    SendMessage(richTextBox1.Handle, EM_FORMATRANGE, IntPtr.Zero, IntPtr.Zero);
}
  1. 调用打印预览
private void btnPrintPreview_Click(object sender, EventArgs e)
{
    PrintPreviewDialog previewDialog = new PrintPreviewDialog();
    previewDialog.Document = printDocument1;
    previewDialog.ShowDialog();
}
注意事项
  • 该实现会完整保留RichTextBox内所有的富文本格式、嵌入图片,分页逻辑和Office Word的打印预览逻辑一致
  • 不需要硬编码坐标位置,会自动适配打印设置里的页边距、纸张大小
  • 如果你需要修改打印的字体大小、样式,直接修改RichTextBox内的内容格式即可,预览会同步更新

内容的提问来源于stack exchange,提问作者Iranga Fernando

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 02:48:00