返回首页

{A}{S0}简介
本文介绍一个RichTextBoxDocument类,允许打印和预览RichTextBox控件的执行。背景
RichTextBox控件是有用的,非常强大。这也很容易使用,并延长使用底层DLL实现它的大部分功能(DLL通常是Riched20.dll的,但可能会有所不同取决于系统)。
在CodeProject上有很多文章,讨论增强标准RichTextBox中,大部分与造型的内容,支持表等的改进命令处理。
在RichTextBox失踪的几个功能之一是能够预览和打印其内容。我最近发布的一篇文章,描述了一个增强PrintPreviewDialog的实施,并人问我用它来预览RichTextBox的文件。这促使我写RichTextBoxDocument类的,这里介绍。
PrintDocument类RichTextBoxDocument继承和实现RichTextBox控件呈现到该文件所需的方法。它可用于标准的PrintPreview和PrintPreviewDialog控制,还与增强PrintPreviewDialog在我原来的文章。使用代码
要使用的RichTextBoxDocument类,开始加入​​到您的项目中的RichTextBoxDocument.cs文件的副本。然后创建一个传递一个引用到RichTextBox控件要呈现的类的实例。 (可选)设置页眉和页脚属性,然后打印或预览文档打印预览对话框或自己的打印文档的方法。
例如:

// create the document passing the RichTextBox to be rendered

var doc = new RichTextBoxDocument(richTextBox1);



// set document header and footer

doc.Header = string.Format("\tDocument {0}", richTextBox1.Name);

doc.Footer = string.Format("{0}\t{1}\tPage [page] of [pages]",

    DateTime.Today.ToShortDateString(),

    DateTime.Now.ToShortTimeString());



// preview the document

using (var dlg = new PrintPreviewDialog())

{

    dlg.Document = doc;

    dlg.ShowDialog(this);

}
RichTextBoxDocument类
RichTextBoxDocument类继承自PrintDocument的渲染方法来呈现一个RichTextBox的内容覆盖了文件。它还允许调用者指定添加到每一页的页眉和页脚。OnPrintPage实施
RichTextBoxDocument类的核心是OnPrintPage RTF格式内容,页眉和页脚的文档的每一页上呈现的方法。代码看起来像这样:
代码执行下列任务:更新当前页的索引。此值可用于在页眉和页脚。使用GetFormatRange和FORMATRANGE方法介绍如下的页面上呈现的RTF内容。渲染这个页面的页眉和页脚。被指定为页眉和页脚,最多可包含三个部分制表符分隔的字符串。二是第一部分是左对齐,居中对齐,最后是右对齐。页眉和页脚可能包含当前页和页计数更换标签(如quot; \ T \ tPage [页] [网页]")。是否有更多的页打印。渲染的RTF
RichTextBox控件周围RICHED20.DLL(或其他一些版本,根据您的系统)的包装。这underlyingnbsp; DLL可以到任何设备使用EM_FORMATRANGE消息呈现其内容。
EM_FORMATRANGE消息允许调用者指定要呈现一个文件的范围,目标设备,和一个目标矩形。它返回的第一个字符的索引,不符合目标矩形,所以你调用者可以继续下页印刷。
RichTextBoxDocument类使用两个辅助方法来包装调用EM_FORMATRANGE消息。第一种方法是所谓GetFormatRange。它创建并初始化一个文档的目标设备和保证金范围FORMATRANGE结构。下面是执行:
// build a FORMATRANGE structure with the proper page size and hdc

// (the hdc must be released after the FORMATRANGE is used)

FORMATRANGE GetFormatRange(PrintPageEventArgs e, int firstChar)

{

    // get page rectangle in twips (RichEd20.dll uses twips)

    var rc = e.MarginBounds;

    rc.X = (int)(rc.X * 14.4 + .5);

    rc.Y = (int)(rc.Y * 14.4 + .5);

    rc.Width = (int)(rc.Width * 14.4 + .5);

    rc.Height = (int)(rc.Height * 14.40 + .5);



    // set up FORMATRANGE structure with the target device/rect

    var fmt = new FORMATRANGE();

    fmt.hdc = fmt.hdcTarget = e.Graphics.GetHdc();

    fmt.rc.SetRect(rc);

    fmt.rcPage = fmt.rc;



    // specify the document range to render

    fmt.cpMin = firstChar;

    fmt.cpMax = -1;



    // done

    return fmt;

}

方法启动缇为单位(RICHED20.DLL使用的计量单位)转换成文档的MarginBounds矩形。然后,它会创建一个FORMATRANGE结构并初始化它与目标设备和目标矩形。最后,它指定要呈现的文件的范围。
文件的范围被指定为字符偏移量,-1表示告诉thenbsp; DLL打印尽可能将适合的目标矩形。
注意GetFormatRange调用GetHdc方法来指定目标设备。后FORMATRANGE结构已被使用,这HDC必须释放与调用ReleaseHdc。
一旦FORMATRANGE结构已准备就绪,我们可以使用它与FORMATRANGE方法来渲染或测量文件。 FORMATRANGE实施很简单,因为它代表所有工作的Riched20.dll:
// send the EM_FORMATRANGE message to the RichTextBox to render or measure

// a range of the document into a target specified by a FORMATRANGE structure.

int FormatRange(RichTextBox rtb, bool render, ref FORMATRANGE fmt)

{

    // render or measure part of the document

    int nextChar = SendMessageFormatRange(

        rtb.Handle,

        EM_FORMATRANGE,

        render ? 1 : 0,

        ref fmt);



    // reset after rendering/measuring

    SendMessage(rtb.Handle, EM_FORMATRANGE, 0, 0);



    // return next character to print

    return nextChar;

}

请注意,每次调用此方法发送EM_FORMATRANG的消息两次。第一个工作(渲染或测量RTF内容)。第二次调用是必需的Riched20.dll本身内部复位。渲染页眉和页脚
这个项目的要求之一是除了为RTF内容呈现在页眉和页脚的能力。这是很容易做到。渲染后的RTF,我们采取一个字符串,把它分解得到左,居中,右对齐的部分,然后使用DrawString方法的页面上绘制。下面的代码显示的代码:
// render a header or a footer on the current page



void RenderHeaderFooter(PrintPageEventArgs e, string text, Font font, Rectangle rc)

{

    var parts = text.Split('\t');



    // render left-aligned part

    if (parts.Length > 0)

        RenderPart(e, parts[0], font, rc, StringAlignment.Near);



    // render center-aligned part

    if (parts.Length > 1)

        RenderPart(e, parts[1], font, rc, StringAlignment.Center);



    // render right-aligned part

    if (parts.Length > 2)

        RenderPart(e, parts[2], font, rc, StringAlignment.Far);

}

这部分的代码分割成三块一个字符串,然后使每一个分别用不同的对齐。这允许用户指定页眉和页脚左,居中,右对齐的部分。下面是RenderPart方法,它的大部分工作的实施:
// special tags for headers/footers

const string PAGE = "[page]";

const string PAGES = "[pages]";



// render a part of a header or footer on the page

void RenderPart(PrintPageEventArgs e, string text, Font font,

                Rectangle rc, StringAlignment align)

{

    // replace wildcards

    text = text.Replace(PAGE, _currentPage.ToString());

    text = text.Replace(PAGES, _pageCount.ToString());



    // prepare string format

    StringFormat fmt = new StringFormat();

    fmt.Alignment = align;

    fmt.LineAlignment = StringAlignment.Center;



    // render footer

    e.Graphics.DrawString(text, font, Brushes.Black, rc, fmt);

}

的代码是直的GDI。有趣的是开始,它取代当前页和页计数的通配符。这使得呼叫者创建页眉和页脚与quot;的mquot N;内容。
这里最棘手的部分是页数。这个值是事先不知道,但幸运的是我们可以计算出,使用FORMATRANGE上面介绍的方法很容易。这里是如何做到这一点:
// get a page count by using FormatRange to measure the content

int GetPageCount(PrintPageEventArgs e)

{

    int pageCount = 0;



    // count the pages using FormatRange

    FORMATRANGE fmt = GetFormatRange(e, 0);

    for (int firstChar = 0; firstChar < _rtb.TextLength; )

    {

        fmt.cpMin = firstChar;

        firstChar = FormatRange(_rtb, false, ref fmt);

        pageCount++;

    }

    e.Graphics.ReleaseHdc(fmt.hdc);



    // done

    return pageCount;

}

注意,第二个参数,在调用FORMATRANGE设置为false。这会导致方法来衡量的内容,但无法呈现。计数的网页这种方式是很简单的速度非常快(测量比渲染速度快了很多)。
计数的网页即使是比较快的,我们只是想做到这一点时实际使用的价值。为此,OnBeginPrint方法扫描来检测是否其中之一使用的网页标记页数,需要更换页眉和页脚字符串:
// start printing the document

protected override void OnBeginPrint(PrintEventArgs e)

{

    // we haven't printed anything yet

    _firstChar = 0;

    _currentPage = 0;



    // check whether we need a page count

    _pageCount = 0;

    if (Header.IndexOf(PAGES) > -1 ||

        Footer.IndexOf(PAGES) > -1)

    {

        _pageCount = -1; // need to calculate this

    }



    // fire event as usual

    base.OnBeginPrint(e);

}

这包括RichTextBoxDocument实施的所有有趣的部分。类很简单,应该很容易为那些需要额外的功能定制。例如,它可能会很有趣,以允许用户指定要打印的列数。预览与该CoolPrintPreview
虽然可以定期PrintPreviewDialog类RichTextBoxDocument类,样本包括本文使用一个CoolPrintPreviewDialog代替。
使用CoolPrintPreviewDialog这里的主要优势是,它显示了,因为它们生成的网页,而标准PrintPreviewDialog需要生成整个文档显示任何之前。这是一个巨大的优势,如果你有超过20页的文件处理。
CoolPrintPreviewDialog源代码包含在便利样本。类中详细描述了在一个单独的CodeProject上的文章,你可以在这里找到:。第二,2009年:10月初始版本

回答

评论会员:瓦利德Hamoudan 时间:2012/01/25
这个代码能够输出影像?



但我不能找到样品中的代码。
,才有可能使每个RichTextBox的图像,说,RichTextBox1的=我们无法找到您所查找的内容等等?
感谢..
评论会员:贝尔纳多Castilho 时间:2012/01/25
如果你的意思是每"页"的文本框的图像检索,那么你就可以做到这一点使用此代码:

public Image GetImage()

{

    // save original print controller

    var pcSave = this.PrintController;

    try

    {

        // create preview controller

        var pcPv = new PreviewPrintController();

        this.PrintController = pcPv;

 

        // print into our controller

        this.Print();

 

        // get page image from controller

        var pageInfo = pcPv.GetPreviewPageInfo();

        if (pageInfo.Length > 0)

        {

            // get image as metafile

            var img = pageInfo[0].Image;

 

            // convert to bitmap (and reduce size)

            return new Bitmap(img, img.Width / 4, img.Height / 4);

        }

    }

    finally

    {

        // restore print controller

        this.PrintController = pcSave;

    }

    return null;

}

此代码返回的图像,在文档的第一页的内容。你会这样调用:

    // add an image of the control to the clipboard

    var doc = new RichTextBoxDocument(richTextBox1);

    Clipboard.SetData(DataFormats.Bitmap, doc.GetImage());

pageInfo变量包含的页面大小和预览图像列表。你可以看一下CoolPrintPreview类项目的更多细节。

希望这是有用的
评论会员:贝尔纳多Castilho 时间:2012/01/25
嗨!贝尔纳
这个类是伟大的工作!我在我的项目中使用它,它工作得很好!
谢谢。
但是我发现一个小错误。至少,我想这是一个。如果您使用不同的文本formatings(字体大小,字体风格,... ...)在一个RichTextBox中的文本的最后一个字符是接近结束几页,有时会出现问题。在这种情况下,它可能是,在打印预览中的实际页码不同的计算页码(页脚)。例如,你得到总数的5页显示在页脚,但只有4页(包括RichTextBox的全部内容)显示在打印预览中。
我想发生这种情况,因为函数GetPageCount调用GetFormatRange只有一次,所有页面的参数firstChar = 0。然而,在功能OnPrintPage,FORMATRANGE每一页的计算与参数firstChar = _firstChar"。因此,到FORMATRANGE装修字符的不同可能会返回一些网页的页数可能会有所不同。

作为一个解决方案,我修改的方式,你可以看到下面的GetPageCount。现在该FORMATRANGE是要求在每个循环周期的正确的参数。现在像在OnPrintPage相同的字符数返回每一页。


int GetPageCount(PrintPageEventArgs e)

{

    int pageCount = 0;

 

    // count the pages using FormatRange

    for (int firstChar = 0; firstChar < _rtb.TextLength; )

    {

        FORMATRANGE fmt = GetFormatRange(e, firstChar);  //THIS LINE WAS MOVED AND MODIFIED

        fmt.cpMin = firstChar;

        firstChar = FormatRange(_rtb, false, ref fmt);

        pageCount++;

        e.Graphics.ReleaseHdc(fmt.hdc);  //THIS LINE WAS MOVED

    }

 

    // done

    return pageCount;

}


希望这是正确的。对于我来说,解决了这个问题为止。

再次感谢!
恩威
评论会员:Thepo 时间:2012/01/25
我恩威看起来不错。感谢分享
评论会员:路易萨默斯 时间:2012/01/25
情况下,它比二进制更真棒
评论会员:游客 时间:2012/01/25
caoqinghuajlhcn:|。这份文件帮了我一个很大起床的速度与我自己的印刷类:|evilt191089
感谢你
评论会员:戴夫表 时间:2012/01/25
一个伟大的想法,以使这样的东西,它帮助了很多))
评论会员:贝尔纳多Castilho 时间:2012/01/25
伟大的控制 - 只是什么我是找对!谢谢!
评论会员:Pedros73 时间:2012/01/25
您正在欢迎{S2}

反馈感谢
评论会员:!贝尔纳Castilho 时间:2012/01/25
同上,。强大的控制
评论会员:夹具沙阿 时间:2012/01/25

评论会员:贝尔纳Castilho 时间:2012/01/25
嗨,

首先感谢所有发展中国家RichTextBox的打印控制。

我有2个笔记本电脑之一是Windows 7和其他有XP。控制工作在Windows 7完美。但不幸的是在Windows XP中,当我点击打印按钮的页数显示在控制移动至134,而不是单页打印预览。
即使我尝试与演示代码...这是投掷32页2-3行的数据...

请指导我,我会错

的问候,
Jignesh上周日,10月18日,2009上午01:56
修改