Knowing the point location of the caret in a Winforms TextBox?
I need to know the exact point location in开发者_开发知识库 my window where the caret currently resides so that I can pop up a small window under the text (similar to intellisense or a spellchecker). The issue is that GetPositionFromCharIndex doesn't seem to work on TextBoxes. This is what I'm trying to do:
Point pt = mTextBox.GetPositionFromCharIndex(mTextBox.SelectionStart);
pt.Y = (int)Math.Ceiling(mTextBox.Font.GetHeight());
mListBox.Location = pt;
However, GetPositionFromCharIndex always returns (0, 0) for TextBoxes (apparently it isn't implemented like it is for RichTextBox). While this function (and this code) works fine for RichTextBoxes, I don't want to migrate the rest of my code from TextBox to RichTextBox except as an absolute LAST resort. There are so many incompatibilities with RichTextBox/TextBox that I'll have to rewrite a very large chunk of my code to do this.
Are there any Winforms wizards out there who know how to do this?
If you don't mind using Win32-API use GetCaretPos to get the exact position.
I have discoverd some peculiar behavior of the textbox control. When you type text at the end of the textbox, GetPositionFromCharIndex remains 0,0. However, if you insert text before the last char in the textbox, GetPositionFromCharIndex IS updated.
Don't know if you can use that to your advantage, but I think you might wanted to know that.
Edit: Ok, LOL, this seems to work, how ugly it may be...:
Point pt = textBox1.GetPositionFromCharIndex(textBox1.SelectionStart > 0 ? textBox1.SelectionStart - 1 : 0);
Edit 2: In retrospect, I think the behavior isn't so odd, since the SelectionStart at the end of a textbox returns the index where the next character will be typed, not where the last character actually is. Since GetPositionFromCharIndex will return an character position, the index of a not yet existing char returns apparently 0,0.
Edit 3: With mine and MikeJ's code combined and slightly reworked, you can get the exact position of the caret:
SizeF size = g.MeasureString(textBox1.Text.Substring(textBox1.SelectionStart - 1, 1), textBox1.Font);
pt.X += (int)size.Width;
I admit, It's damn-ugly the way I typed it, but with some editing you should be able to get some nice code out of this.
Attach the following code snippet in the TextBox.KeyDown
event to locate the screen point of the cursor within the text box control:
using (Graphics g = Graphics.FromHwnd(textBox1.Handle))
{
SizeF size = g.MeasureString(textBox1.Text.Substring(0, textBox1.SelectionStart), textBox1.Font);
Point pt = textBox1.PointToScreen(new Point((int)size.Width, (int)0));
label1.Text = "Manual: " + pt.ToString();
}
The code snippet above takes into account the font used by the TextBox
control to correctly calculate the actual length of the string up to the cursor position within the control. When performing an "Intellisense" style of auto complete popup, it's important to know the actual screen position in order to popup the "Auto completion" list.
That's what the snippet does. It calculates the width of the text, then converts that with into a "local" coordinate to the TextBox control and from there converts that point into a relevant screen coordinate.
One caveat to consider: the SelectionStart
is delayed and not immediately updated. This is due in part to how the text box control handles keyboard input.
TextBox lbl = new TextBox();
lbl.Text = Build("20000", i);
lbl.Location = new Point(30, 25 * i);
lbl.Width = 350;
lbl.MouseHover += new EventHandler(lbl_MouseHover);
void lbl_MouseHover(object sender,
EventArgs e)
{
TextBox t = (TextBox)sender;
ListBox lb = new ListBox();
for (int i = 0; i < 10; i++)
{
lb.Items.Add("Hej");
}
int x = t.Location.X;
int y = t.Location.Y + t.Height;
lb.Location = new Point(x, y);
panel1.Controls.Add(lb);
lb.BringToFront();
}
Note this particular piece of code: int y = t.Location.Y + t.Height; You add the height of the textbox to the y-axis.
MessageBox.Show(textBox1.SelectionStart.ToString());
Try this.
Under `Control.KeyPress` event,
I use Control.GetPositionFromCharIndex(Control.SelectionStart)
to get the caret position which relate to the control. Then translate this position relate to the screen for the form's new location.
private void aTextBox_KeyPress(object sender, KeyPressEventArgs e)
{
SuperComboForm supcomboF = new SuperComboForm();
supcomboF.StartPosition = FormStartPosition.Manual;
var caret_pos = aTextBox.GetPositionFromCharIndex(aTextBox.SelectionStart);
supcomboF.Location = PointToScreen(new Point(caret_pos.X, caret_pos.Y));
supcomboF.ShowDialog();
}
Here is my solution:
private Point GetCaretPosition()
{
Point result = TextBox.GetPositionFromCharIndex(TextBox.SelectionStart);
if (result.X == 0 && TextBox.Text.Length > 0)
{
result = TextBox.GetPositionFromCharIndex(TextBox.Text.Length - 1);
int s = result.X / TextBox.Text.Length;
result.X = (int)(result.X + (s * 1.3));
}
return result;
}
精彩评论