{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控件要呈现的类的实例。 (可选)设置页眉和页脚属性,然后打印或预览文档打印预览对话框或自己的打印文档的方法。
例如:
RichTextBoxDocument类// 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类继承自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月初始版本