Reputation: 1365
I would like to know how I can get the word that current cursor is on, in WPF RichTextBox
. I am aware that RichTextBox
has Selection Property. However, this only gives me the text that is highlighted in the RichTextBox
. Instead I would like to know the word the cursor is on even if the whole word is not highlighted.
Any tips are appreciated.
Upvotes: 6
Views: 3752
Reputation: 81430
Here is my alternative solution using LINQ and Dependency Property:
public class SelectionRichTextBox : RichTextBox
{
public SelectionRichTextBox()
{
// Use base class style
SetResourceReference(StyleProperty, typeof(RichTextBox));
}
public static readonly DependencyProperty SelectedWordProperty =
DependencyProperty.Register(
"SelectedWord",
typeof(string),
typeof(SelectionRichTextBox),
new PropertyMetadata("")
);
public string SelectedWord
{
get
{
return (string)GetValue(SelectedWordProperty);
}
set
{
SetValue(SelectedWordProperty, value);
}
}
protected override void OnMouseUp(MouseButtonEventArgs e)
{
TextPointer cursorPosition = CaretPosition;
string strBeforeCursor = cursorPosition.GetTextInRun(LogicalDirection.Backward);
string strAfterCursor = cursorPosition.GetTextInRun(LogicalDirection.Forward);
string wordBeforeCursor = strBeforeCursor.Split().Last();
string wordAfterCursor = strAfterCursor.Split().First();
string text = wordBeforeCursor + wordAfterCursor;
SelectedWord = string.Join("", text
.Where(c => char.IsLetter(c))
.ToArray());
base.OnMouseUp(e);
}
}
After that, you can use it in binding like this:
<custom:SelectionRichTextBox
SelectedWord="{Binding SelectedWord, Mode=OneWayToSource}"/>
Upvotes: -1
Reputation: 1505
Since words are divided by spaces, you can iterate through the runs around the caret until space is found. This function should work even when your RichTextBox
even contains different Fonts and Font Sizes.
public string GetWordByCaret(LogicalDirection direction)
{
// Get the CaretPosition
TextPointer position = this.CaretPosition;
TextPointerContext context = position.GetPointerContext(direction);
string text = string.Empty;
// Iterate through the RichTextBox based on the Start, Text and End of nearby inlines
while (context != TextPointerContext.None)
{
// We are only interested in the text here
//, so ignore everything that is not text
if (context == TextPointerContext.Text)
{
string current = position.GetTextInRun(direction);
// The strings appended based on whether they are before the caret or after it...
// And well...I love switches :)
switch (direction)
{
case LogicalDirection.Backward:
{
int spaceIndex = current.LastIndexOf(' ');
// If space is found, we've reached the end
if (spaceIndex >= 0)
{
int length = current.Length - 1;
if (spaceIndex + 1 <= length)
{
text = current.Substring(spaceIndex + 1, length - spaceIndex) + text;
}
return text;
}
else
text = current + text;
}
break;
default:
{
int spaceIndex = current.IndexOf(' ');
// If space is found, we've reached the end
if (spaceIndex >= 0)
{
int length = current.Length;
if (spaceIndex <= length)
{
text += current.Substring(0, spaceIndex);
}
return text;
}
else
text += current;
}
break;
}
}
// Move to the next position
position = position.GetNextContextPosition(direction);
// Get the next context
if (position != null)
context = position.GetPointerContext(direction);
else
context = TextPointerContext.None;
}
return text;
}
Now you can get the word you caret is on like this.
string before = GetWordByCaret(LogicalDirection.Backward);
string after = GetWordByCaret(LogicalDirection.Forward);
string word = before + after; // :)
Upvotes: -1
Reputation: 2929
Attach this function to an arbitrary RichTextBox, now called testRTB, and see Output window for results:
private void testRTB_MouseUp(object sender, MouseButtonEventArgs e)
{
TextPointer start = testRTB.CaretPosition; // this is the variable we will advance to the left until a non-letter character is found
TextPointer end = testRTB.CaretPosition; // this is the variable we will advance to the right until a non-letter character is found
String stringBeforeCaret = start.GetTextInRun(LogicalDirection.Backward); // extract the text in the current run from the caret to the left
String stringAfterCaret = start.GetTextInRun(LogicalDirection.Forward); // extract the text in the current run from the caret to the left
Int32 countToMoveLeft = 0; // we record how many positions we move to the left until a non-letter character is found
Int32 countToMoveRight = 0; // we record how many positions we move to the right until a non-letter character is found
for (Int32 i = stringBeforeCaret.Length - 1; i >= 0; --i)
{
// if the character at the location CaretPosition-LeftOffset is a letter, we move more to the left
if (Char.IsLetter(stringBeforeCaret[i]))
++countToMoveLeft;
else break; // otherwise we have found the beginning of the word
}
for (Int32 i = 0; i < stringAfterCaret.Length; ++i)
{
// if the character at the location CaretPosition+RightOffset is a letter, we move more to the right
if (Char.IsLetter(stringAfterCaret[i]))
++countToMoveRight;
else break; // otherwise we have found the end of the word
}
start = start.GetPositionAtOffset(-countToMoveLeft); // modify the start pointer by the offset we have calculated
end = end.GetPositionAtOffset(countToMoveRight); // modify the end pointer by the offset we have calculated
// extract the text between those two pointers
TextRange r = new TextRange(start, end);
String text = r.Text;
// check the result
System.Diagnostics.Debug.WriteLine("[" + text + "]");
}
Change Char.IsLetter(...) to Char.IsLetterOrDigit(...) or whatever else appropriately depending on whether you wish to keep digits as well.
Tip: extract this into an extension method in a separate assembly to access it whenever needed.
Upvotes: 5
Reputation: 1365
OK so in order to solve this I brute forced it.
I used
curCaret.GetTextInRun(LogicalDirection.Backward)
and
curCaret.GetTextInRun(LogicalDirection.Forward)
along with preCaretString.LastIndexOf(" ")
and postCaretString.IndexOf(" ")
plus other dividers that separates word and got the substrings.
Eventually I added the first half of string and second half of string to obtain the currently cursored word.
I bet there are cleverer way of doing this but at least this solved the problem
Upvotes: 3
Reputation: 2436
You can get the current position of the cursor via CaretPosition
.
Unfortunately there is no easy way to get the characters to the left/right of the caret position. The only way I know of to get text out of a RichTextBox is in this answer, which is a bit convoluted. But it will accomplish what is necessary.
Upvotes: 1