在 TextSelectionChanged 上保留单词索引
Preserve word index on TextSelectionChanged
我有一个查找下一个和上一个功能并对其进行了编辑,以便当用户 select 在文本框中输入文本并单击“查找下一个”或“查找上一个”按钮时,查找功能将启动它的从 selected 字符索引并浏览每个搜索结果(最初该功能不存在)。为了获取 selected 文本的起始索引,我创建了一个函数:
private int GetIntialCharPos(string Text)
{
int row = Variables._TextBox.GetLineIndexFromCharacterIndex(Variables._TextBox.CaretIndex);
int col = Variables._TextBox.CaretIndex - Variables._TextBox.GetCharacterIndexFromLineIndex(row);
return col;
}
查找下一个和上一个的函数如下:
private List<int> _matches;
private string _textToFind;
private bool _matchCase;
private int _matchIndex;
private void MoveToNextMatch(string textToFind, bool matchCase, bool forward)
{
if (_matches == null || _textToFind != textToFind || _matchCase != matchCase)
{
int startIndex = 0, matchIndex;
StringComparison mode = matchCase ? StringComparison.CurrentCulture : StringComparison.CurrentCultureIgnoreCase;
_matches = new List<int>();
while (startIndex < Variables._TextBox.Text.Length && (matchIndex = Variables._TextBox.Text.IndexOf(textToFind, startIndex, mode)) >= 0)
{
_matches.Add(matchIndex);
startIndex = matchIndex + textToFind.Length;
}
_textToFind = textToFind;
_matchCase = matchCase;
_matchIndex = forward ? _matches.IndexOf(GetIntialCharPos(textToFind)) : _matches.IndexOf(GetIntialCharPos(textToFind)) - 1;
}
else
{
_matchIndex += forward ? 1 : -1;
if (_matchIndex < 0)
{
_matchIndex = _matches.Count - 1;
}
else if (_matchIndex >= _matches.Count)
{
_matchIndex = 0;
}
}
if (_matches.Count > 0)
{
Variables._TextBox.SelectionStart = _matches[_matchIndex];
Variables._TextBox.SelectionLength = textToFind.Length;
Variables._TextBox.Focus();
}
}
我的问题是,一旦用户 select 编辑了他需要搜索的文本,并通过查找下一个和上一个按钮,然后他决定 select 来自不同的索引,而不是从 selected 索引继续搜索,它将保持默认的初始顺序,而不是从 selected 索引开始并遍历每个结果。我创建了一个小 gif video here 以便您可以更好地了解这个问题。
如何保留 selected 的单词索引,以便每次用户 select 来自不同索引时,它都可以从用户 select 所在的索引开始搜索ed 而不是总是从头开始。
private int _matchIndex;
那是你的问题变量。它保留了最后一个匹配索引,但您不知道用户何时自行更改它。 TextBox class 没有 SelectionChanged 事件来告诉您它,因此没有简单的方法来重置变量。
只需使用 RichTextBox 代替,它确实有 that event。
但这要容易得多,出现此错误是因为您通过将搜索操作拆分为单独的 class 不必要地添加了状态。状态通常是一件坏事,它是一个错误生成器。您可以通过直接使用 TextBox 对象轻松地使整个操作无状态:
private static void MoveToNextMatch(TextBoxBase box, bool forward) {
var needle = box.SelectedText;
var haystack = box.Text;
int index = box.SelectionStart;
if (forward) {
index = haystack.IndexOf(needle, index + 1);
if (index < 0) index = haystack.IndexOf(needle, 0);
}
else {
if (index == 0) index = -1;
else index = haystack.LastIndexOf(needle, index - 1);
if (index < 0) index = haystack.LastIndexOf(needle, haystack.Length - 1);
}
if (index >= 0) {
box.SelectionStart = index;
box.SelectionLength = needle.Length;
}
box.Focus();
}
使用采用 StringComparison 的 Last/IndexOf() 方法来实现 matchCase 参数。
我有一个查找下一个和上一个功能并对其进行了编辑,以便当用户 select 在文本框中输入文本并单击“查找下一个”或“查找上一个”按钮时,查找功能将启动它的从 selected 字符索引并浏览每个搜索结果(最初该功能不存在)。为了获取 selected 文本的起始索引,我创建了一个函数:
private int GetIntialCharPos(string Text)
{
int row = Variables._TextBox.GetLineIndexFromCharacterIndex(Variables._TextBox.CaretIndex);
int col = Variables._TextBox.CaretIndex - Variables._TextBox.GetCharacterIndexFromLineIndex(row);
return col;
}
查找下一个和上一个的函数如下:
private List<int> _matches;
private string _textToFind;
private bool _matchCase;
private int _matchIndex;
private void MoveToNextMatch(string textToFind, bool matchCase, bool forward)
{
if (_matches == null || _textToFind != textToFind || _matchCase != matchCase)
{
int startIndex = 0, matchIndex;
StringComparison mode = matchCase ? StringComparison.CurrentCulture : StringComparison.CurrentCultureIgnoreCase;
_matches = new List<int>();
while (startIndex < Variables._TextBox.Text.Length && (matchIndex = Variables._TextBox.Text.IndexOf(textToFind, startIndex, mode)) >= 0)
{
_matches.Add(matchIndex);
startIndex = matchIndex + textToFind.Length;
}
_textToFind = textToFind;
_matchCase = matchCase;
_matchIndex = forward ? _matches.IndexOf(GetIntialCharPos(textToFind)) : _matches.IndexOf(GetIntialCharPos(textToFind)) - 1;
}
else
{
_matchIndex += forward ? 1 : -1;
if (_matchIndex < 0)
{
_matchIndex = _matches.Count - 1;
}
else if (_matchIndex >= _matches.Count)
{
_matchIndex = 0;
}
}
if (_matches.Count > 0)
{
Variables._TextBox.SelectionStart = _matches[_matchIndex];
Variables._TextBox.SelectionLength = textToFind.Length;
Variables._TextBox.Focus();
}
}
我的问题是,一旦用户 select 编辑了他需要搜索的文本,并通过查找下一个和上一个按钮,然后他决定 select 来自不同的索引,而不是从 selected 索引继续搜索,它将保持默认的初始顺序,而不是从 selected 索引开始并遍历每个结果。我创建了一个小 gif video here 以便您可以更好地了解这个问题。
如何保留 selected 的单词索引,以便每次用户 select 来自不同索引时,它都可以从用户 select 所在的索引开始搜索ed 而不是总是从头开始。
private int _matchIndex;
那是你的问题变量。它保留了最后一个匹配索引,但您不知道用户何时自行更改它。 TextBox class 没有 SelectionChanged 事件来告诉您它,因此没有简单的方法来重置变量。
只需使用 RichTextBox 代替,它确实有 that event。
但这要容易得多,出现此错误是因为您通过将搜索操作拆分为单独的 class 不必要地添加了状态。状态通常是一件坏事,它是一个错误生成器。您可以通过直接使用 TextBox 对象轻松地使整个操作无状态:
private static void MoveToNextMatch(TextBoxBase box, bool forward) {
var needle = box.SelectedText;
var haystack = box.Text;
int index = box.SelectionStart;
if (forward) {
index = haystack.IndexOf(needle, index + 1);
if (index < 0) index = haystack.IndexOf(needle, 0);
}
else {
if (index == 0) index = -1;
else index = haystack.LastIndexOf(needle, index - 1);
if (index < 0) index = haystack.LastIndexOf(needle, haystack.Length - 1);
}
if (index >= 0) {
box.SelectionStart = index;
box.SelectionLength = needle.Length;
}
box.Focus();
}
使用采用 StringComparison 的 Last/IndexOf() 方法来实现 matchCase 参数。