如何将长文本拆分为多个 pieces/pages 以适应 RichTextBox?

How to split a long text into multiple pieces/pages to fit into a RichTextBox?

在开发 WinForm 应用程序时,我想将一个长文本(只有字符,没有图像,没有特殊格式)拆分成多个部分(页面),以便将它们放入 RichTextBox.

问题是RichTextBox不支持多页。

我必须编写自己的算法(某种 DP 编程)来拆分文本。

目前我正在使用TextRenderer.MeasureText()来检查一个字符串是否适合富文本框,例如:

bool canfit(String str)
{
    Size rbx_size = new Size(rbx.Width, rbx.Height);
    Size sz = TextRenderer.MeasureText(str, rbx.font, rbx_size, TextFormatFlags.WordBreak | TextFormatFlags.GlyOverhangPadding)
    return (sz.Height < rbx_size.Height);
}

效果不是很好,因为我觉得上面的函数不准确。

我想知道是否有简单的方法来做到这一点?谢谢。

我不确定,我没有测试代码,但我希望它可以帮助你,至少会给你一个想法。

将字体大小 pt 转换为 px

Float points = 72; // each point in pixels 
Float fontSize = 12; // font size
Float dpi = 96; // find screen dpi (96 is very common I know)
Float px = fontsize * dpi / points; // each lines need px (exp: 16)

查找文本中的行数(exp:200 行)

var lineCount = File.ReadAllLines(@"C:\file.txt").Length;
var lineCount = 0;
using (var reader = File.OpenText(@"C:\file.txt"))
{
    while (reader.ReadLine() != null)
    {
        lineCount++;
    }
}

我们在第 1 步和第 2 步找到了行数和每行需要多少像素,因此我们可以找到分隔符位置来标记相关行中的文本

Float totalTextPixels = lineCount * px;
Float textFieldHeight = 200; // for example
Float eachSplitLine = totalTextPixels / textFieldHeight;

再次循环行

var lineCount = File.ReadAllLines(@"C:\file.txt").Length;
var lineCount = 0;
using (var reader = File.OpenText(@"C:\file.txt"))
{
    while (reader.ReadLine() != null)
    { 
        lineCount++;
        if(lineCount == eachSplitLine){
            // Put a deliminiter here (I don't know how)
            // for example | (pipe)
            lineCount = 0;
        }
    }
}

并用 |

拆分您的文本

我知道它不稳健,教授。解决方案,这只是一个想法...

祝你生活愉快。

提出的问题,按照我的解释,是将源文档的文本拆分为多个子字符串(Pages),例如每个子字符串可以放入一个 RichTextBox 客户区, 没有 spawning/requiring 滚动条。

因此,第一个任务是计算给定文档必须拆分成的Chuncks/Pages个数。

TextRenderer Class 方法是常用的工具。
在执行计算时,必须指示 class 换行文本(如 RichTextBox 控件所做的那样),排除任何填充并使字符串适合 TextBox 类型控件的编辑区域。

主要 TextFormatFlags 标志将是:

TextFormatFlags.WordBreak | TextFormatFlags.NoPadding | TextFormatFlags.TextBoxControl;

下一个任务是计算有多少 letters/symbols 适合 RichTextBox 控件的工作区,这样就不需要滚动条了。

Winforms RichTextBox 控件不提供任何特定工具,但非特定工具足以执行此计算。

As a note, the [Lines[] property][3] (derived from TextBoxBase) reports only the original text line splitting (intended as line feeds), not the lines generated by the internal word-wrapping.

但是,TextBoxBase GetFirstCharIndexFromLine() and GetLineFromCharIndex() 可以组合使用以获得相同的结果:
知道 ClientArea 可以容纳多少行,下一行的第一个字符将比 ClientArea 中最后一个可见行的最后一个字符多一个字符(空格不换行)。
然后,在有足够的行填充 ClientArea 时重复此过程。最后将包含[Total Lines] / [Lines per Page]的提醒。

此过程结果的可视化表示:

执行计算大小和拆分源文档文本的基本任务的示例代码。用于创建示例图像的完整源代码:(PasteBin - RTB Reader)

string Document = "[SomeDocumentText]";
List<string> Pages = new List<string>();
TextFormatFlags flags = TextFormatFlags.Top | TextFormatFlags.Left | TextFormatFlags.WordBreak | 
                        TextFormatFlags.NoPadding | TextFormatFlags.TextBoxControl;

Size textSize = TextRenderer.MeasureText(Document, richTextBox1.Font, richTextBox1.ClientSize, flags);
int numberOfPages = textSize.Height / richTextBox1.ClientSize.Height;

if (textSize.Height > richTextBox1.Height) {
    richTextBox1.Text = Document;
    richTextBox1.Update();

    int firstCharOfLastShownLine = richTextBox1.GetCharIndexFromPosition(new Point(0, richTextBox1.ClientSize.Height));
    int visibleLines = richTextBox1.GetLineFromCharIndex(firstCharOfLastShownLine);
    int totalLines = richTextBox1.GetLineFromCharIndex(richTextBox1.Text.Length - 1);

    for (int p = 0; p < numberOfPages; p++) {
        int firstLineOfPage = (p * visibleLines);
        int firstCharOfPage = richTextBox1.GetFirstCharIndexFromLine(firstLineOfPage);

        int firstLineOfNextPage = (p + 1) * visibleLines;
        firstLineOfNextPage = (firstLineOfNextPage > totalLines) ? totalLines : firstLineOfNextPage;
        int lastCharOfPage = (firstLineOfNextPage < totalLines)
                           ? richTextBox1.GetFirstCharIndexFromLine(firstLineOfNextPage) - 1
                           : richTextBox1.Text.Length;
        Pages.Add(richTextBox1.Text.Substring(firstCharOfPage, lastCharOfPage - firstCharOfPage));
    }
}
else {
    Pages.Add(Document);
}
richTextBox1.Text = Pages.First();