我正在使用以下Form,其中richTextBox 是有问题的RichTextBox,RichTextBox_SelectionChanged 是SelectionChanged event 处理程序,我们将尝试使用它来解决我们的问题。
public MainForm()
{
InitializeComponent();
this.richTextBox.Rtf =
@"{\rtf1\ansi\ansicpg1252\deff0\deflang2057{\fonttbl{\f0\fnil\fcharset0 Microsoft Sans Serif;}}\viewkind4\uc1\pard\f0\fs17 My \v upgraded \v0 control that \v hopefully \v0 will make it\par}";
this.richTextBox.SelectionChanged += RichTextBox_SelectionChanged;
}
基本上,这个想法很简单 - 使用 SelectionChanged 处理程序正确地将 Select 隐藏数据与先前的选择放在一起。
为此,我们必须存储之前的选择数据:
private class SelectionData
{
public static SelectionData FromStartAndEnd(
Int32 start,
Int32 end)
{
return new SelectionData(
start: start,
length: end - start);
}
public SelectionData(TextBoxBase tb)
: this(
start: tb.SelectionStart,
length: tb.SelectionLength)
{ }
public SelectionData(Int32 start, Int32 length)
{
this.Start = start;
this.Length = length;
}
public readonly Int32 Start, Length;
public Int32 End
{
get
{
return this.Start + this.Length;
}
}
}
在某些领域:
private SelectionData _previousSelection;
并更新/修复 SelectionChanged 处理程序中的选择
private void RichTextBox_SelectionChanged(object sender, EventArgs e)
{
var newSelection = new SelectionData(this.richTextBox);
this.SelfUpdateSelection(newSelection);
}
SelfUpdateSelection 方法类似于:
private Boolean _isSelectionSelfUpdating = false;
private void SelfUpdateSelection(SelectionData newSelection)
{
if (!this.IsKeyBoardSelection())
{
// Or it will use previous selection when we don't need it.
this._previousSelection = null;
return;
}
if (this._isSelectionSelfUpdating)
return;
this._isSelectionSelfUpdating = true;
try
{
var fixedSelection = this.FixSelection(newSelection);
this.richTextBox.Select(
start: fixedSelection.Start,
length: fixedSelection.Length);
this._previousSelection = fixedSelection;
}
finally
{
this._isSelectionSelfUpdating = false;
}
}
IsKeyBoardSelection 为简单起见可能类似于以下内容,但正确检测选择更改源会更加困难:
private bool IsKeyBoardSelection()
{
// It may not be true, but usually close enough.
return Control.ModifierKeys.HasFlag(Keys.Shift);
}
FixSelection 方法应该比较newSelection 是否可以是this._previousSelection,并创建一个新的SelectionData,其中将包含newSelection、this._previousSelection 以及它们之间的隐藏数据。
你可以这样使用:
private SelectionData FixSelection(SelectionData newSelection)
{
if (this._previousSelection == null)
return newSelection;
var start = Math.Min(
newSelection.Start,
this._previousSelection.Start);
var end = Math.Max(
newSelection.End,
this._previousSelection.End);
return SelectionData.FromStartAndEnd(
start: start,
end: end);
}
但它:
- 仅适用于向前(右箭头)选择 - 可以通过向
FixSelection 添加一些额外的逻辑来修复。
-
还需要一点额外的this._previousSelection 处理(比如在 FocusLost 事件中重置它) - 有一些边缘情况,但仍然没有什么不可能的。
public MainForm()
{
...
this.richTextBox.LostFocus += RichTextBox_LostFocus;
}
private void RichTextBox_LostFocus(object sender, EventArgs e)
{
this._previousSelection = null;
}
PS: 为简单起见,我在表单中实现了所有内容,包括字段和表单级处理程序,但通过一些努力,它可以变成可重用的东西(最坏的情况是派生 RichTextBox,最好的情况是一些将为RichTextBox 提供此类处理的外部组件。