Discussion:
Hi All, How to get "Word ToolTip" feature in a TextBox
Andy Langowitz
2003-12-05 14:21:48 UTC
Permalink
I'm looking for a way to have a "Word ToolTip" feature in
a TextBox or RichTextBox.
That is, for example, hover (NOT select) the mouse over
a word and see that word's definition right next to the
word.
Couldn't resist spending a half hour preparing an answer to this one. I'm
not sure how far back to start, so I'm going to assume you know nothing
and take it one step at a time. If you'd like to try it out as you go,
open a new C# Windows Application project and drop a RichTextBox control
on your Form.

Q: How do I add tool tip functionality to a RichTextBox?

A: Drop a ToolTip control onto your form, and a new property will appear
in the property sheet of the RichTextBox for tooltip text.

Q: How do I change the text of the tooltip at runtime?

A: Invoke the SetToolTip method on the tooltip control, passing it the
RichTextBox as the first argument, and the tooltip text as the second
argument. Example:

string sToolTip = "here is some text set at runtime";
toolTip1.SetToolTip(richTextBox1, sToolTip);

Q: How do I make the text of the tooltip dependent on the mouse position
within the RichTextBox?

A: Add a handler for the MouseMove event of the RichTextBox and call the
SetToolTip method from there. (In your case, this code should extract the
word at the event's mouse position from the RichTextBox and look up the
definition, but I'm getting to that). Example:

private void richTextBox1_MouseMove(object sender,
System.Windows.Forms.MouseEventArgs e)
{
string sTip = String.Format ("X={0}, Y={1}", e.X, e.Y);
toolTip1.SetToolTip(richTextBox1, sTip);
}

Q: How do I determine at what index in the text the current mouse position
is?

A: Call the GetCharIndexFromPosition method of the RichTextBox. Example:

richTextBox1.GetCharIndexFromPosition(new Point(e.X, e.Y));

Q: How do I extract a word from the text at a given index?

A: Now we're down to basic string manipulation. Here is my final code,
including a helper function to extract a word from the text (you will
probably want additional separators):

private void richTextBox1_MouseMove(object sender,
System.Windows.Forms.MouseEventArgs e)
{
string sTip = ExtractWord (richTextBox1.Text,
richTextBox1.GetCharIndexFromPosition(new Point(e.X, e.Y)));
toolTip1.SetToolTip(richTextBox1, sTip);
}

private static char[] s_acSeparators = {' ', '\n'};

private string ExtractWord (string sText, int iPos)
{
//get the position of the beginning of the word (if no separator
found, this gives zero)
int iStart = sText.LastIndexOfAny(s_acSeparators, iPos) + 1;
//get the position of the separator after the word
int iEnd = sText.IndexOfAny(s_acSeparators, iPos);
if (iEnd < 0)
iEnd = sText.Length;

if (iEnd < iStart)
return "";
return sText.Substring(iStart, iEnd - iStart);
}

Now all you need to do is look up the word in your dictionary.

HTH,

Andy Langowitz
Andy Langowitz
2003-12-05 15:12:56 UTC
Permalink
I just realized my previous example included some bad code, that I don't
want people to emulate: it is inefficient to create objects in a mouse
move event handler. The original code created a new temporary Point
object on each mouse move event:

//*** BAD CODE
private void richTextBox1_MouseMove(object sender,
System.Windows.Forms.MouseEventArgs e)
{
string sTip = ExtractWord (richTextBox1.Text,
richTextBox1.GetCharIndexFromPosition(new Point(e.X, e.Y)));
toolTip1.SetToolTip(richTextBox1, sTip);
}

A corrected version, which creates the Point object only once:

private Point m_oPoint = new Point();

private void richTextBox1_MouseMove(object sender,
System.Windows.Forms.MouseEventArgs e)
{
m_oPoint.X = e.X;
m_oPoint.Y = e.Y;
string sTip = ExtractWord (richTextBox1.Text,
richTextBox1.GetCharIndexFromPosition(m_oPoint));
toolTip1.SetToolTip(richTextBox1, sTip);
}

Andy Langowitz
Eugene Mortimore
2003-12-07 09:50:46 UTC
Permalink
Hi Andy,

Andy, Thanks Sooo Much! for you wonderful code on my ToolTip
in RichTextBox question - worked beautifully.

Andy, just one question:

Is there any way to get rid of the multiple ToolTip
graphic as you drag the mouse (quickly!) in the
RichTextBox from one word to another?

Thanks
Gene
Rob Perkins
2003-12-08 05:11:20 UTC
Permalink
Post by Andy Langowitz
A: Add a handler for the MouseMove event of the RichTextBox and call the
SetToolTip method from there.
Uh, why not use the "MouseHover" event?

Rob
Andy Langowitz
2003-12-08 14:13:03 UTC
Permalink
Post by Eugene Mortimore
Is there any way to get rid of the multiple ToolTip
graphic as you drag the mouse (quickly!) in the
RichTextBox from one word to another?
By a bit of experimenting, I have discovered a fix that improves things.
The problem is caused by the following behavior of the ToolTip control: if
you change the tooltip for a control in code at runtime while the mouse is
over the control, the tooltip activates immediately, regardless of the
InitialDelay property of the tooltip. A workaround is to deactivate the
tooltip momentarily while you set its text in code. However, for some
reason, when I do this, I fail to see any tooltip at all, unless I
suppress redundant SetToolTip calls when the mouse moves over a given word.

The bottom line - replace the line that does the SetToolTip with the
following:

if (!sTip.Equals(toolTip1.GetToolTip(richTextBox1)))
{
toolTip1.Active = false;
toolTip1.SetToolTip(richTextBox1, sTip);
toolTip1.Active = true;
}

Andy
Andy Langowitz
2003-12-09 14:32:05 UTC
Permalink
Post by Rob Perkins
Uh, why not use the "MouseHover" event?
Because MouseHover only gets an EventArgs (without X and Y coordinates)
whereas MouseMove gets a MouseEventArgs.

Also, I wasn't sure how the MouseHover timing mechanism would interact
with the ToolTip delay mechanism, and I wanted to keep it simple.

Yes, I know I can use MousePosition to get the current coordinates, but
you can get subtle weirdnesses if you process a mouse event using
coordinates other than those recorded with the event. Would it be safe in
this context?

Andy
Rob Perkins
2003-12-09 18:21:27 UTC
Permalink
Post by Andy Langowitz
Post by Rob Perkins
Uh, why not use the "MouseHover" event?
Because MouseHover only gets an EventArgs (without X and Y coordinates)
whereas MouseMove gets a MouseEventArgs.
Also, I wasn't sure how the MouseHover timing mechanism would interact
with the ToolTip delay mechanism, and I wanted to keep it simple.
Yes, I know I can use MousePosition to get the current coordinates, but
you can get subtle weirdnesses if you process a mouse event using
coordinates other than those recorded with the event. Would it be safe in
this context?
Well, you've seen that modifying the ToolTip displays it without regard to the delay mechanism. So, what you do is use a couple of form variables or class variables to store the word the mouse is over in OnMouseMove, but you use OnMouseHover to make the ToolTip visible. (Or in a VB context you could use event handlers the IDE gives you, but the approach is the same)

Maybe something like this:

Public Class f
Inherits Form

Private ToolTipText as String

' ...

Private Sub f_MouseMove(args) Handles Form.MouseMove
' compute what goes in the ToolTip HERE
End Sub

Private Sub f_MouseHover(args) Handles Form.MouseHover
' make tooltip visible HERE, by assigning ToolTipText to its Text property.
End Sub

End Class


Something like that?

Rob

Loading...