【问题标题】:Printing RichTextBox打印 RichTextBox
【发布时间】:2015-11-29 22:21:36
【问题描述】:

我正在制作一个简单的 WinForms 应用程序,我希望允许用户从 RichTextBox 打印文本。

然后我关注了MSDN link.. 它适用于真正的打印机(真正的我是指我可以触摸的打印机:))

但是如果我想使用某种 PDF 打印机怎么办? 那么我必须说它在只打印一页 时有效。 每一个下一页都被打印在第一页上,这意味着文本被套印了。 这很明显,但我能做些什么来强制 PDF 打印机创建一个新页面?

这是我的代码:

private PrintDocument docToPrint; 
private string stringToPrint;

public mainForm()
        {
            InitializeComponent();
            CenterToScreen();
            this.docToPrint = new PrintDocument();
            (...)
        }

private void tsBtnPrint_Click(object sender, EventArgs e)
        {
            PrintDialog myPrintDialog = new PrintDialog();
            myPrintDialog.AllowCurrentPage = true;
            myPrintDialog.AllowSelection = true;
            myPrintDialog.AllowSomePages = true;
            myPrintDialog.Document = docToPrint;
            if(myPrintDialog.ShowDialog()==DialogResult.OK)
            {
                StringReader reader = new StringReader(this.richTextBox.Text);
                stringToPrint = reader.ReadToEnd();
                this.docToPrint.PrintPage += new PrintPageEventHandler(this.docToPrintCustom);
                this.docToPrint.Print();
            }
        }

 private void docToPrintCustom(object sender, PrintPageEventArgs e)
        {
            Font PrintFont = this.richTextBox.Font;
            SolidBrush PrintBrush = new SolidBrush(Color.Black); 

            int LinesPerPage = 0;
            int charactersOnPage = 0;

            e.Graphics.MeasureString(stringToPrint, PrintFont, e.MarginBounds.Size, StringFormat.GenericTypographic,
                out charactersOnPage, out LinesPerPage);

            e.Graphics.DrawString(stringToPrint, PrintFont, PrintBrush, e.MarginBounds, StringFormat.GenericTypographic);

            stringToPrint = stringToPrint.Substring(charactersOnPage);

            MessageBox.Show(stringToPrint.Length.ToString());
            e.HasMorePages = (stringToPrint.Length > 0);

            PrintBrush.Dispose();
        }

我应该怎么做才能以正确的方式打印每一页?

【问题讨论】:

  • 您能否检查一下您是否只在代码中设置了一次PrintPage 事件?如果您多次设置它可能会发生这种情况(如果您的代码基于 MSDN 示例,那么构造函数中的“(...)”可能会有另一个分配)。
  • 嗨 :) 是的,我只设置了一次 PrintPage 事件,如代码所示。 (...) 中没有其他赋值。
  • 很奇怪。它适用于某些 PDF 打印机,其中一些不适用。我不知道有什么我可以做的。
  • 只执行一次:this.docToPrint.PrintPage += new PrintPageEventHandler 设置打印处理程序。每次单击按钮时都会添加它,因此每次单击按钮时您的代码都会运行多次。您不需要那个 StringReader,只需 stringToPrint = this.richTextBox.Text; 学习使用调试器并逐步检查代码以查看发生了什么的美好一天。 MessageBox 是穷人的调试器——停止使用它。
  • @LarsTech 谢谢你的好建议,我真的很感激:)

标签: c# .net winforms printing richtextbox


【解决方案1】:

您可以通过向其发送EM_FORMATRANGE 消息来打印您的RuchTextBox 的内容。

此消息通常用于格式化富编辑的内容 对打印机等输出设备的控制。

要实施此解决方案,您可以执行以下步骤:

  • 首先创建一个继承自RichTextBoxRichtextBoxEx 类并实现一个FormatRange 方法,它的工作是打印控件内容的每一页。下面列出了完整的代码。
  • 然后创建一个Form并在其上放置一个RichTextBoxExPrintDocument并处理PrintDocumentBeginPrintPrintPageEndPrint事件,并使用以下代码进行打印。

请注意

  1. 使用这种方式,您可以打印应用到文本的所有格式的控件内容,这比使用一种字体和大小以黑色打印所有文本要好。您还可以将内容的字体和大小设置为一种字体、大小和颜色。

  2. 创建RichtextBoxEx 只是为了封装并且是完全可选的,如果你想使用你现有的控件,你可以使用我在下面提供的FormatRange 方法的内容并将你的控件的属性传递给它来执行打印.

支持打印的 RichTextBoxEx:

这是我上面描述的完整工作代码。代码摘自 Martin Müller 的 msdn 文章 Getting WYSIWYG Print Results from a .NET RichTextBox 文章。

public class RichTextBoxEx : RichTextBox
{
    [StructLayout(LayoutKind.Sequential)]
    private struct STRUCT_RECT
    {
        public Int32 left;
        public Int32 top;
        public Int32 right;
        public Int32 bottom;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct STRUCT_CHARRANGE
    {
        public Int32 cpMin;
        public Int32 cpMax;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct STRUCT_FORMATRANGE
    {
        public IntPtr hdc;
        public IntPtr hdcTarget;
        public STRUCT_RECT rc;
        public STRUCT_RECT rcPage;
        public STRUCT_CHARRANGE chrg;
    }

    [DllImport("user32.dll")]
    private static extern Int32 SendMessage(IntPtr hWnd, Int32 msg, Int32 wParam, IntPtr lParam);

    private const Int32 WM_USER = 0x400;
    private const Int32 EM_FORMATRANGE = WM_USER + 57;
    private const Int32 EM_GETCHARFORMAT = WM_USER + 58;
    private const Int32 EM_SETCHARFORMAT = WM_USER + 68;

    /// <summary>
    /// Calculate or render the contents of our RichTextBox for printing
    /// </summary>
    /// <param name="measureOnly">If true, only the calculation is performed, otherwise the text is rendered as well</param>
    /// <param name="e">The PrintPageEventArgs object from the PrintPage event</param>
    /// <param name="charFrom">Index of first character to be printed</param>
    /// <param name="charTo">Index of last character to be printed</param>
    /// <returns> (Index of last character that fitted on the page) + 1</returns>
    public int FormatRange(bool measureOnly, PrintPageEventArgs e, int charFrom, int charTo)
    {
        // Specify which characters to print
        STRUCT_CHARRANGE cr = default(STRUCT_CHARRANGE);
        cr.cpMin = charFrom;
        cr.cpMax = charTo;

        // Specify the area inside page margins
        STRUCT_RECT rc = default(STRUCT_RECT);
        rc.top = HundredthInchToTwips(e.MarginBounds.Top);
        rc.bottom = HundredthInchToTwips(e.MarginBounds.Bottom);
        rc.left = HundredthInchToTwips(e.MarginBounds.Left);
        rc.right = HundredthInchToTwips(e.MarginBounds.Right);

        // Specify the page area
        STRUCT_RECT rcPage = default(STRUCT_RECT);
        rcPage.top = HundredthInchToTwips(e.PageBounds.Top);
        rcPage.bottom = HundredthInchToTwips(e.PageBounds.Bottom);
        rcPage.left = HundredthInchToTwips(e.PageBounds.Left);
        rcPage.right = HundredthInchToTwips(e.PageBounds.Right);

        // Get device context of output device
        IntPtr hdc = default(IntPtr);
        hdc = e.Graphics.GetHdc();

        // Fill in the FORMATRANGE structure
        STRUCT_FORMATRANGE fr = default(STRUCT_FORMATRANGE);
        fr.chrg = cr;
        fr.hdc = hdc;
        fr.hdcTarget = hdc;
        fr.rc = rc;
        fr.rcPage = rcPage;

        // Non-Zero wParam means render, Zero means measure
        Int32 wParam = default(Int32);
        if (measureOnly)
        {
            wParam = 0;
        }
        else
        {
            wParam = 1;
        }

        // Allocate memory for the FORMATRANGE struct and
        // copy the contents of our struct to this memory
        IntPtr lParam = default(IntPtr);
        lParam = Marshal.AllocCoTaskMem(Marshal.SizeOf(fr));
        Marshal.StructureToPtr(fr, lParam, false);

        // Send the actual Win32 message
        int res = 0;
        res = SendMessage(Handle, EM_FORMATRANGE, wParam, lParam);

        // Free allocated memory
        Marshal.FreeCoTaskMem(lParam);

        // and release the device context
        e.Graphics.ReleaseHdc(hdc);

        return res;
    }

    /// <summary>
    /// Convert between 1/100 inch (unit used by the .NET framework)
    /// and twips (1/1440 inch, used by Win32 API calls)
    /// </summary>
    /// <param name="n">Value in 1/100 inch</param>
    /// <returns>Value in twips</returns>
    private Int32 HundredthInchToTwips(int n)
    {
        return Convert.ToInt32(n * 14.4);
    }

    /// <summary>
    /// Free cached data from rich edit control after printing
    /// </summary>
    public void FormatRangeDone()
    {
        IntPtr lParam = new IntPtr(0);
        SendMessage(Handle, EM_FORMATRANGE, 0, lParam);
    }
}

使用示例:

private void printDocument1_BeginPrint(object sender, System.Drawing.Printing.PrintEventArgs e)
{
    // Start at the beginning of the text
    firstCharOnPage = 0;
}

private void printDocument1_EndPrint(object sender, System.Drawing.Printing.PrintEventArgs e)
{
    // Clean up cached information
    richTextBoxEx1.FormatRangeDone();
}

private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
    firstCharOnPage = richTextBoxEx1.FormatRange(false, e, firstCharOnPage, richTextBoxEx1.TextLength);
    // check if there are more pages to print
    if (firstCharOnPage < richTextBoxEx1.TextLength)
        e.HasMorePages = true;
    else
        e.HasMorePages = false;
}

private void printToolStripButton_Click(object sender, EventArgs e)
{
    //Print the contents here
    printDocument1.Print();
}

【讨论】:

  • 代码看起来像是从Getting WYSIWYG Print Results from a .NET RichTextBox 提取的。您的答案应该反映代码的来源。
  • @LarsTech 感谢您将我指向原始源代码 :) 答案基于我在 10 多年前在旧应用程序中使用的源代码,我不记得在哪里是源代码的起源。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-17
  • 2013-01-23
  • 1970-01-01
  • 1970-01-01
  • 2012-10-29
相关资源
最近更新 更多