Note: code examples here have been tested, and verified, in Visual Studio 2013, compiled against .NET FrameWork 4.5, using the 'AnyCPU build option on a Win 8/64 machine.
After some research, I've found there is a known problem with using 'GetPositionFromCharIndex to determine the caret position (insertion cursor point) in a TextBox
under certain circumstances. However, 'GetPositionFromCharIndex works fine with a RichTextBox.
With a TextBox:
To
reliably get the caret point in a TextBox, it is necessary to use a WinAPI call:
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetCaretPos(out Point lpPoint);
Here's the original code I posted modifed to use the WinAPI 'GetCaretPos function:
private Point GetCursorTextBox(TextBox theTextBox)
{
if (this.ActiveControl != theTextBox)
{
this.ActiveControl = theTextBox;
}`
Point chrTbLocation;
GetCaretPos(out chrTbLocation);
int selStart = theTextBox.SelectionStart + theTextBox.SelectionLength;
int line = theTextBox.GetLineFromCharIndex(selStart);
Point chrScrnLocation = theTextBox.PointToScreen(chrTbLocation);
Point chrFrmLocation = this.PointToClient(chrScrnLocation);
int charPosition = textBox1.GetCharIndexFromPosition(chrTbLocation);
Console.WriteLine("Caret at: Line: {0} Column: {1} in TextBox: {2}", line, selStart, theTextBox.Name);
Console.WriteLine("Screen Location: {0} Form Location: {1}", chrScrnLocation, chrFrmLocation);
Console.WriteLine();
return chrFrmLocation;
}
Note that at the start of the method, a check is done to determine if the TextBox argument to the method is the current ActiveControl on the Form; if it is not, then the TextBox argument is made the ActiveControl. This is done because the call to 'GetCaretPos has no way of "knowing" which Control on the Form, if any, currently is the ActiveControl.
But, also, note that if you invoke the 'GetCursorTextBox method from an EventHandler of the TextBox passed in as an argument, such as in the TextChanged EventHandler, then that check is not really necessary because the TextChanged event is only going to be raised when the TextBox is the ActiveControl.
If having the ActiveControl changed by the 'GetCursorTextBox method presents a problem for you, then preserve the current ActiveControl at the start of the method, and set it back at the end of the method. Code to do this is shown commented out.
With a RichTextBox:
This code will work fine with a RichTextBox:
private Point GetCursorRichTextBox(RichTextBox theRichTextBox)
{
int selStart = theRichTextBox.SelectionStart + theRichTextBox.SelectionLength;
int line = theRichTextBox.GetLineFromCharIndex(selStart);
Point chrTbLocation = theRichTextBox.GetPositionFromCharIndex(selStart);
Point chrScrnLocation = theRichTextBox.PointToScreen(chrTbLocation);
Point chrFrmLocation = this.PointToClient(chrScrnLocation);
int charPosition = theRichTextBox.GetCharIndexFromPosition(chrTbLocation);
Console.WriteLine("Caret at: Line: {0} Column: {1} in RichTextBox: {2}", line, selStart, theRichTextBox.Name);
Console.WriteLine("Screen Location: {0} Form Location: {1}", chrScrnLocation, chrFrmLocation);
Console.WriteLine();
return chrFrmLocation;
}