set cursor position in RIchTextBox
Anyone know how this can be done? I saw an earlier post here:
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=365476&SiteID=1
that states that you can assign a value to rtb.SelectionStart, but you can no longer do that now. SO how can it be done with the current build of WPF?
[472 byte] By [
Kommi] at [2008-1-4]
You can do it by setting rtb.CaretPosition or by setting rtb.SelectionStart or rtb.Selection.Select()
What makes you think that rtb.SelectionStart no longer works?
Siri Vellanki - MSFT wrote: |
| You can do it by setting rtb.CaretPosition or by setting rtb.SelectionStart or rtb.Selection.Select() What makes you think that rtb.SelectionStart no longer works? | |
Unless I am really missing something here, I do not see any property or method for RichTextBox class (System.Windows.Controls) called SelectionStart. There is a rtb.Selection.Start but it is read only. rtb.Selection.Select() requires 2 textPointers as parameters. but that means that I first need to increment the TextPointer some how, and then pass it to Select(). So still dont know how to move the cursor foward since I cant find a way to increment the TextPointer.
The easiest way to set the caret's position is to set RichTextBox.CaretPosition = position. This is functionally equivalent to calling RichTextBox.Select(position, position).
No matter which approach you use, you'll still need to learn how to work with TextPointers. RichTextBox does not operate on indices like TextBox does. TextBox only contains text, so indices are sufficient there. A RichTextBox can contain other TextElements-- formatting, images, hyperlinks-- which makes index-based addressing inadequate. RichTextBox therefore uses TextPointers to navigate its content.
To advance the caret to the next caret position, you would do something like this:
TextPointer moveTo = myRichTextBox.CaretPosition.GetNextInsertionPosition(LogicalDirection.Forward);
if (moveTo != null)
{
myRichTextBox.CaretPosition = moveTo;
}
To go backward instead of forward, just using LogicalDirection.Backward instead.
You might wonder, "What is an insertion position, anyway?" From the docs:
An insertion position is a position where new content may be added without breaking any semantic rules for the associated content. In practice, an insertion position is anywhere in content where a caret may be positioned.
There are many other ways to manipulate TextPointers. For more information, see the docs at http://msdn2.microsoft.com/en-us/library/system.windows.documents.textpointer_members.aspx
- Peter
RichTextBox rtb = new RichTextBox();TextPointer tp = rtb.CaretPosition;tp = tp.GetNextInsertionPosition(
LogicalDirection.Forward);rtb.Selection.Select(rtb.CaretPosition, tp);
the aboe selects one char starting from the current caret position... if you want to select somewhere other that the caret position, you can pass that pointer in instead of the caretposition.
HTH
OK that is making me understand what is going on a bit better. My problem is that I cannot still get it to function the way I need to.
let say I have a line of text:
Code Snippet
<Paragraph><Run>max went to the store</Run><Run FontWeight="Bold" Background="#FFFFFF00">to buy some fruit</Run></Paragraph>
and the cursor is currently on the word "store". How do I then move it to after "...fruit</run>" so as to insert some text?
Code Snippet
TextPointer tp = RTB.CaretPosition.Paragraph.ContentEnd.GetNextInsertionPosition(LogicalDirection.Backward);
OK almost, but my example should have been more general. I know about ContentEnd. But what if the text is this:
Code Snippet
<run>max went to the store</RUN><RUN Background="#FFFFFF00" FontWeight="Bold">to buy some fruit</RUN><RUN>and some meat</RUN><RUN>and some eggs</RUN><RUN>and a car</RUN><RUN>and a newspaper</RUN></PARAGRAPH>
and I wanted to move the carrot from "fruit</Run>" to "<run>and some eggs"? In general I am having trouble figuring out how I move the carrot around from one set of tags to the other.
TextPointer has API to help navigate through the document.
Please refer to http://msdn2.microsoft.com/en-us/library/system.windows.documents.textpointer_members.aspx
Examples:
textPointer.GetInsertionPosition(logicalDirection)
textPointer.GetNextInsertionPosition(logicalDirection)
textPointer.GetPositionAtOffset(offset, logicalDirection)
textPointer.GetNextContextPosition(logicalDirection)
Also you can use other helpers to know where you are like...
TextPointer.GetPointerContext(logicalDirection)
Yes I am aware of all of those methods. Unfortunately, I guess my problem lies in the bahavior of the TextPointer itself. Take the following code for example
Code Snippet
DataBox.BeginChange();
TextPointer startPosition = DataBox.Selection.Start;Run r = new
Run("text to insert"
, startPosition); DataBox.EndChange();
startPosition is the current location of the carrot in my RichTextBox. I have debugged this code and found the following:
first off if you look at the non public members of startPosition there is a private field called charOffset which holds the index location of the DataBox.Selection.Start.
OK so If I run my app and there is no other text in the RichTextBox and the carot is on the first position, then if this code is executed the startPointer has an intial char offset of 0, and then has one of 14, the length of the string I inserted. So the TextPointer moved foward by itself after the insertion. No probs there.
BUT, if I type in some text first, say "hello world ", and then exeucte that code, then something different happens. startPosition first has a char offset of 12, which is the right edge of the "hello world " string. But after the code in executed and the "text to insert" is inserted, the new char off set IS STILL 12!!! Why is that? Why does the startPosition not move if there is already text in the RichTextBox? Honestly, this is driving me nuts. I wish I could just use all the TextPointer methods to move it around, if only the damn pointer remained consistent in its behavior!
Ok I am going to include my example:
Window1.xaml
Code Snippet
<Window x:Class="TextPointerTest.Window1"
xmlns=
"http://schemas.microsoft.com/winfx/2006/xaml/presentation
" xmlns:x=
"http://schemas.microsoft.com/winfx/2006/xaml
" Title=
"TextPointerTest
" Height=
"300
" Width=
"300
" >
<Grid>
<RichTextBox Margin="18,16,13,126" Name="rtb" />
<Button Height="23" Margin="18,0,0,94" Name="b1" xmlns:onVerticalAlignment="http://schemas.microsoft.com/winfx/2006/xaml/presentation" VerticalAlignment="Bottom" HorizontalAlignment="Left" Width="75">Insert text</< FONT>Button>
</< FONT>Grid>
</< FONT>Window>
Window1.xaml.cs
Code Snippet
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace TextPointerTest
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary> publicpartialclass
Window1 : System.Windows.Window {
public
Window1(){
InitializeComponent();
RoutedCommand insertCommand = new
RoutedCommand("insertCommand"
, typeof
(Window1));//this will overwrite the default crtl+r command for the richTextBox and replace with my cutom command
rtb.InputBindings.Add(
new
InputBinding(insertCommand,new
KeyGesture(Key.R, ModifierKeys.Control)));//add an event handler to the command and then add it to the DataBox
CommandBinding binding = new
CommandBinding(insertCommand);binding.Executed +=
new
ExecutedRoutedEventHandler(this
.insertText);rtb.CommandBindings.Add(binding);
}
publicvoid
insertText(object
sender, RoutedEventArgs args){
//rtb.BeginChange(); //absolutely neccesary!!!
TextPointer startPosition = rtb.CaretPosition.GetPositionAtOffset(0, LogicalDirection.Forward);Run r = new
Run("text to insert"
, startPosition); }
}
}
if you run this code, and then use the crtl+R command to insert text, notice where the carot is. First type something then hit crtl+r, then just start the app again and do the crtl+r first. You will see what I am confused about
The culprit here is LogicalDirection.
Every TextPointer, including the CaretPosition, has a LogicalDirection of Forward or Backward. Think of it as gravity. When text is inserted at a TextPosition, imagine that position bouncing up into the air. Gravity-- LogicalDirection-- then pulls that position forward or backward as it falls, so that it winds up either at the front (Forward) or back (Backward) of the newly-inserted text. The reasons for this behavior are a bit beyond the scope of this response, because it involves bidirectional text (a left-to-right language like English mixed with a right-to-left language like Hebrew).
Fortunately, this is under your control. You can't set the LogicalDirection on a TextPointer, but you can create a new TextPointer at the same position and add your text to that:
Code Snippet
TextPointer forward = rtb.CaretPosition.GetPositionAtOffset(0, LogicalDirection.Forward);
Run r = new Run("text to insert", forward);
Note that you don't need to set the CaretPosition at all to do this (as long as the only behavior you care about is through your custom insertText method. If you want to affect the gravity of a normal Paste, you'd need to set the caret-- but if the user types or navigates the caret, the editor may override your setting).
- Peter
Hey Peter, thanks for the reply but I tried using your code and the problem still exists. PLEASE NOTE I have changed the code in my post above to use your method of getting the pointer.
I just made the observation that the concept of "foward" for the TextPointer changes if you use the arrow keys to move the cursor arround. So if I type some text in, then hit foward on the curosr and hit crtl+r to insert the text, the cursor will not move to the positon after the insertion. But if I hit back on the cursor and the hit crtl+r then the cursor goes to the position after the inserted text. Is this a bug?
Edit:
OK upon further debugging I have noticed the following: TextPointer has a non public member called "Edge" that differs depedning on which direction the cursor was traveling in the RichTextBox
if the cursor was going left to right (ie typing a word or navigating using the arrow keys), then the TextPointer object looks like the following:
http://www.kommi.com/max/BeforeEnd.jpg
if the cursor was going right to left (in my case I was moving it using the arrow keys, but I can imagine that typing in hebrew or arabic would have done the same) then the TextPointer object looks like this
http://www.kommi.com/max/BeforeStart.jpg
Notice the area I highlighted. Notice how the logicDirection is still "Foward" for both of them, but it looks like which diretion is "foward" has changed. When googling the System.Windows.Documents.ElementEdge, the only page that comes up is this:
http://msdn2.microsoft.com/en-us/library/aa480168.aspx saying that it is deprecated.
There must be a way to tell the RichTextBox or the TextPointer which direction foward is. Anyone got any ideas or suggestions?
Try this code:
Code Snippet
rtb.BeginChange();
TextPointer forward = rtb.CaretPosition.GetPositionAtOffset(0, LogicalDirection.Forward);
forward.InsertTextInRun("text to insert");
rtb.CaretPosition = forward;
rtb.EndChange();
yeah I tried that before and it did not work. For anyone who would like to help, here is the demo project in a compressed rar file: http://www.kommi.com/TextPointerTest.rar
As stated earlier, once you run it you press crtl+r to insert a piece of text. But hitting the arrow keys left and right before pressing crtl+r will cause the caret to end up in different positions, either before or after the insert. I am trying to find a way to control this behavior, so that the caret always ends up after the inserted text, no matter what.
The code I posted above works in your app when I try it. The key difference is the use of InsertTextInRun instead of creating a new run and adding it.