C#打印带水印RichTextBox时文本背景不透明问题排查
问题根因
调用EM_FORMATRANGE消息渲染RichTextBox内容时,默认会先使用控件当前的BackColor属性值(默认为白色)填充整个目标渲染矩形区域,直接覆盖预先绘制的水印背景图,这就是文本区域显示不透明白底、无法透出下方水印的核心原因。
修复方案
两种方案可以组合使用,兼容所有Windows系统版本:
- 临时修改控件背景色:在触发打印渲染前,临时将RichTextBox的
BackColor设置为Color.Transparent,渲染完成后立刻还原为原始背景色。注意RichTextBox本身不支持常驻透明背景,仅在打印流程中临时修改即可,避免界面显示异常。 - 设置GDI设备上下文透明模式:在获取打印DC后、发送
EM_FORMATRANGE消息前,调用Win32 API将DC的背景混合模式设为透明,跳过渲染时的白底填充步骤。
具体代码修改
- 新增需要用到的Win32 API声明和常量:
[DllImport("gdi32.dll")] private static extern int SetBkMode(IntPtr hdc, int iBkMode); private const int TRANSPARENT_BK_MODE = 1;
- 修改
printDocument_PrintPage方法,增加背景色临时切换逻辑,同时修复原代码中图片资源未释放的泄漏问题:
private void printDocument_PrintPage(object sender, PrintPageEventArgs e) { int documentWidth = (int)(e.PageSettings.PrintableArea.Width - 0.5); int documentHeight = (int)(e.PageSettings.PrintableArea.Height - 0.5); // 绘制水印背景,using释放资源避免句柄泄漏 using (Image bgImg = Image.FromFile("bg.jpeg")) { e.Graphics.DrawImage(bgImg, new Rectangle(0, 0, documentWidth, documentHeight), new Rectangle(0, 0, bgImg.Width, bgImg.Height), GraphicsUnit.Pixel); } // 临时切换背景色为透明 Color originalBkColor = control.BackColor; control.BackColor = Color.Transparent; try { m_nFirstCharOnPage = FormatRange(false, e, m_nFirstCharOnPage, control.TextLength); } finally { // 无论渲染是否出错,都还原原始背景色 control.BackColor = originalBkColor; } e.HasMorePages = m_nFirstCharOnPage < control.TextLength; }
- 修改
FormatRange方法,在获取HDC后增加透明模式设置:
public int FormatRange(bool measureOnly, PrintPageEventArgs e, int charFrom, int charTo) { STRUCT_CHARRANGE cr; cr.cpMin = charFrom; cr.cpMax = charTo; STRUCT_RECT rc; rc.top = HundredthInchToTwips(10); rc.bottom = HundredthInchToTwips((int)e.PageSettings.PrintableArea.Height - 20); rc.left = HundredthInchToTwips(20); rc.right = HundredthInchToTwips((int)e.PageSettings.PrintableArea.Width - 10); STRUCT_RECT rcPage; rcPage.top = HundredthInchToTwips(e.PageBounds.Top); rcPage.bottom = HundredthInchToTwips(e.PageBounds.Bottom); rcPage.left = HundredthInchToTwips(e.PageBounds.Left); rcPage.right = HundredthInchToTwips(e.PageBounds.Right); IntPtr hdc = e.Graphics.GetHdc(); // 设置DC背景为透明,跳过白底填充 SetBkMode(hdc, TRANSPARENT_BK_MODE); STRUCT_FORMATRANGE fr; fr.chrg = cr; fr.hdc = hdc; fr.hdcTarget = hdc; fr.rc = rc; fr.rcPage = rcPage; Int32 wParam = measureOnly ? 0 : 1; IntPtr lParam = Marshal.AllocCoTaskMem(Marshal.SizeOf(fr)); Marshal.StructureToPtr(fr, lParam, false); int res = SendMessage(control.Handle, EM_FORMATRANGE, wParam, lParam); Marshal.FreeCoTaskMem(lParam); e.Graphics.ReleaseHdc(hdc); return res; }
补充说明:原代码中
PrintRTF方法里强制设置A4纸尺寸的逻辑,建议增加判断,优先使用打印机默认的纸张设置,避免用户选择其他尺寸纸张时出现内容裁切问题。
内容的提问来源于stack exchange,提问作者Ahmed Shamel
相关产品推荐
相关产品推荐

