如果存在隐藏文本,WinForms RichTextBox 选择问题
WinForms RichTextBox selection issue if there is hidden text
我有一个 selectRichTextBox 控件的问题。如果控件包含隐藏文本,selection 会表现得很奇怪。
- 如果我用鼠标 selection,有时会出现错误,有时不会。特别是如果我 select 更多行错误似乎消失了。
- 但是如果用户尝试使用键盘select文本,这个错误会非常烦人。
问题如下:假设我的控件有以下文本:
There is the little upgraded control that hopefully will make a
differnce when it is hidden text the reason
然后假设我们通过应用适当的 RTF 标签来隐藏单词 upgraded, hopefully, hidden
:
@"{\rtf1\ansi\ansicpg1252\deff0\deflang2057{\fonttbl{\f0\fnil\fcharset0 Microsoft Sans Serif;}} \viewkind4\uc1\pard\f0\fs17 There is the little \v upgraded \v0 control that \v hopefully \v0 will make a differnce when it is \v hidden \v0 text the reason\par}";
一切看起来都不错,但是当用户尝试使用键盘 select 文本时,selection 似乎每次到达隐藏词时都会重置。
我的控件包含隐藏文本是至关重要的(一些来自我的对象的重要 ID 在控件内形成内容作为隐藏文本存储在特殊位置,我可以't/don'我不想改变它)。
我正在使用以下 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;
和update/fix选择里面的SelectionChanged
hanlder
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;
}
P.S.: 为简单起见,我已经在表单中实现了带有字段和 form-level 处理程序的所有内容,但通过一些努力,它可以变成一些可重用的东西(最坏的派生 RichTextBox
,最好的一些外部组件将为 RichTextBox
提供这样的处理)。
我有一个 selectRichTextBox 控件的问题。如果控件包含隐藏文本,selection 会表现得很奇怪。
- 如果我用鼠标 selection,有时会出现错误,有时不会。特别是如果我 select 更多行错误似乎消失了。
- 但是如果用户尝试使用键盘select文本,这个错误会非常烦人。
问题如下:假设我的控件有以下文本:
There is the little upgraded control that hopefully will make a differnce when it is hidden text the reason
然后假设我们通过应用适当的 RTF 标签来隐藏单词 upgraded, hopefully, hidden
:
@"{\rtf1\ansi\ansicpg1252\deff0\deflang2057{\fonttbl{\f0\fnil\fcharset0 Microsoft Sans Serif;}} \viewkind4\uc1\pard\f0\fs17 There is the little \v upgraded \v0 control that \v hopefully \v0 will make a differnce when it is \v hidden \v0 text the reason\par}";
一切看起来都不错,但是当用户尝试使用键盘 select 文本时,selection 似乎每次到达隐藏词时都会重置。
我的控件包含隐藏文本是至关重要的(一些来自我的对象的重要 ID 在控件内形成内容作为隐藏文本存储在特殊位置,我可以't/don'我不想改变它)。
我正在使用以下 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;
和update/fix选择里面的SelectionChanged
hanlder
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; }
P.S.: 为简单起见,我已经在表单中实现了带有字段和 form-level 处理程序的所有内容,但通过一些努力,它可以变成一些可重用的东西(最坏的派生 RichTextBox
,最好的一些外部组件将为 RichTextBox
提供这样的处理)。