.NET 2.0 中 RichTextBox 行号显示






4.67/5 (38投票s)
2005年11月3日
3分钟阅读

292472

12244
标准的 RichTextBox 不允许显示行号。这个用户控件可以实现。
引言
在文本编辑器中显示行号是一个常见的功能。但是 .NET 2.0 中的标准 RichTextBox
不支持此功能。而且在互联网上很难找到合适的解决方案。尤其是,一个不直接使用 Win32 函数的解决方案。
RichTextBox
不是 Windows Forms 中的标准控件。它没有像应该的那样使用 OnPaint
方法和其他类似函数,并且还隐藏了一些正确定制所需的重要属性。克服这个问题的一种方法是使用 win32 API 函数并重写 WndProc 函数。我不喜欢这种做法,但我不得不一次又一次地使用它。我认为这是 .NET 开发人员的错。
幸运的是,对于 RichTextBox
的行号显示,有一个令人满意的解决方案,它不使用纯 Win32 API 函数。
实现
我们将带有行号的 RichTextBox
实现为一个 UserControl
。我们不会重写 RichTextBox
中的任何内容,只会使用它的事件。我们的 UserControl
名为 NumberedTextBoxUC
,由 SplitContainer
、Label
(numberLabel
) 和 RichTextBox
组成。Label
用于显示行号,RichTextBox
用于显示文本内容,两者都包含在 SplitterContainer
中。
numberLabel
的内容在 RichTextBox
的事件处理程序中更新。这些事件是
OnTextChanged
OnVScroll
OnSizeChanged
OnFontChanged
问题
此实现存在几个问题。第一个是滚动。与 VS 源代码编辑器或 TextBox
控件不同,RichTextBox
使用平滑滚动,因此使用滚动条滚动文本以像素为单位,而不是以行为单位。您会注意到第一行显示了一半。这并不总是需要的行为,我希望可以关闭它。另一个问题是大型 Label
的重绘速度,您不能在每个 OnTextChanged
事件处理程序中将太多行打印到 Label
。另一个奇怪的问题是 .NET 2.0 中的 RichTextBox
使用奇怪的行缩进,无法关闭或设置为零。当控件顶部对齐时,Label
和 RichTextBox
中的相同字体会导致不同的行位置。
解决方案
我只显示可见行的行号,因此不会打印不必要的隐藏行号。更新函数称为 updateNumberLabel()
。它使用 RichTextBox
函数 GetCharIndexFromPosition
和 GetLineFromCharIndex
来确定第一个和最后一个可见行号。
private void updateNumberLabel()
{
//we get index of first visible char and
//number of first visible line
Point pos = new Point(0, 0);
int firstIndex = richTextBox1.GetCharIndexFromPosition(pos);
int firstLine = richTextBox1.GetLineFromCharIndex(firstIndex);
//now we get index of last visible char
//and number of last visible line
pos.X = ClientRectangle.Width;
pos.Y = ClientRectangle.Height;
int lastIndex = richTextBox1.GetCharIndexFromPosition(pos);
int lastLine = richTextBox1.GetLineFromCharIndex(lastIndex);
//this is point position of last visible char, we'll
//use its Y value for calculating numberLabel size
pos = richTextBox1.GetPositionFromCharIndex(lastIndex);
//finally, renumber label
numberLabel.Text = "";
for (int i = firstLine; i <= lastLine + 1; i++)
{
numberLabel.Text += i + 1 + "\n";
}
}
对于不同的行缩进,我找到了一个常数,它最适合字体大小 8。Label
字体的大小对于这个常数来说更大。我希望在未来的 .NET 版本中,这个问题能够得到修复,并且两种字体将完全相同,就像在 .NET 1.1 中一样。
public NumberedTextBoxUC()
{
InitializeComponent();
numberLabel.Font = new Font(richTextBox1.Font.FontFamily,
richTextBox1.Font.Size + 1.019f);
}
平滑滚动是另一个导致很多麻烦的问题。我在每个 OnVScroll
事件处理程序中使用小的 numberLabel
位置更新。Label
移动的像素数与 RichTextBox
字体高度的倍数不同,因此是字体高度的模数文本位置。在我看来,如果不使用 Win32 函数,反向解决方案(将文本位置更新为行对齐)是不可能的。以这种方式使用 RichTextBox
函数更新文本位置会导致文本抖动。
private void richTextBox1_VScroll(object sender, EventArgs e)
{
//move location of numberLabel for amount
//of pixels caused by scrollbar
int d = richTextBox1.GetPositionFromCharIndex(0).Y %
(richTextBox1.Font.Height + 1);
numberLabel.Location = new Point(0, d);
updateNumberLabel();
}
结论
我希望这个用户控件能帮助开发人员处理 RichTextBox
。我感谢您的建议和对此控件的改进。