I want to display a tooltip when the mouse hovers over a link in my custom rich edit control. Consider the following text:
We all sleep at night .
In my case the word sleep is a link.
When the user moves the mouse under the link, in this case "sleep", I want to display a tooltip for the link.
The following came to my mind, but they are not working
1) Trapping OnMouseHover
if(this.Cursor == Cursors.Hand)
tooltip.Show(textbox,"My tooltip");
else
tooltip.Hide(textbox);
But this does not work out.
UPDATE
The links mentioned are not URLs, i.e these are custom links, so Regex won't be of much help here, it can be any text. The user can choose to create it a a link.
Though I have not tried GetPosition method, I dont think it would be that elegant in terms of design and maintenance.
Let me say I have the following line, in my richedit box
We sleep at night. But the bats stay awake. Cockroaches become active at night.
In the above sentence, I want three different tooltips, when the mouse hovers over them.
sleep -> Human beings
awake -> Nightwatchman here
active -> My day begins
I trapped OnMouseMove as follows:
Working- with Messagebox
OnMouseMove( )
{
// check to see if the cursor is over a link
// though this is not the correct approach, I am worried why does not a tooltip show up
if(this.Cursor.current == Cursors.hand )
{
Messagebox.show("you are under a link");
}
}
Not Working - with Tooltip - Tooltip does not show up
OnMouseMove( MouseventArgs e )
{
if(cursor.current == cursors.hand )
{
tooltip.show(richeditbox,e.x,e.y,1000);
}
}
Just add ToolTip tool from toolbox to the form and add this code in a mousemove event of any control you want to make the tooltip start on its mousemove
private void textBox3_MouseMove(object sender, MouseEventArgs e)
{
toolTip1.SetToolTip(textBox3,"Tooltip text"); // you can change the first parameter (textbox3) on any control you wanna focus
}
hope it helps
peace
Well, take a look, this works, If you have problems please tell me:
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1() { InitializeComponent(); }
ToolTip tip = new ToolTip();
void richTextBox1_MouseMove(object sender, MouseEventArgs e)
{
if (!timer1.Enabled)
{
string link = GetWord(richTextBox1.Text, richTextBox1.GetCharIndexFromPosition(e.Location));
//Checks whether the current word i a URL, change the regex to whatever you want, I found it on www.regexlib.com.
//you could also check if current word is bold, underlined etc. but I didn't dig into it.
if (System.Text.RegularExpressions.Regex.IsMatch(link, #"^(http|https|ftp)\://[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(:[a-zA-Z0-9]*)?/?([a-zA-Z0-9\-\._\?\,\'/\\\+&%\$#\=~])*$"))
{
tip.ToolTipTitle = link;
Point p = richTextBox1.Location;
tip.Show(link, this, p.X + e.X,
p.Y + e.Y + 32, //You can change it (the 35) to the tooltip's height - controls the tooltips position.
1000);
timer1.Enabled = true;
}
}
}
private void timer1_Tick(object sender, EventArgs e) //The timer is to control the tooltip, it shouldn't redraw on each mouse move.
{
timer1.Enabled = false;
}
public static string GetWord(string input, int position) //Extracts the whole word the mouse is currently focused on.
{
char s = input[position];
int sp1 = 0, sp2 = input.Length;
for (int i = position; i > 0; i--)
{
char ch = input[i];
if (ch == ' ' || ch == '\n')
{
sp1 = i;
break;
}
}
for (int i = position; i < input.Length; i++)
{
char ch = input[i];
if (ch == ' ' || ch == '\n')
{
sp2 = i;
break;
}
}
return input.Substring(sp1, sp2 - sp1).Replace("\n", "");
}
}
}
You shouldn't use the control private tooltip, but the form one. This example works well:
public partial class Form1 : Form
{
private System.Windows.Forms.ToolTip toolTip1;
public Form1()
{
InitializeComponent();
this.components = new System.ComponentModel.Container();
this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
MyRitchTextBox myRTB = new MyRitchTextBox();
this.Controls.Add(myRTB);
myRTB.Location = new Point(10, 10);
myRTB.MouseEnter += new EventHandler(myRTB_MouseEnter);
myRTB.MouseLeave += new EventHandler(myRTB_MouseLeave);
}
void myRTB_MouseEnter(object sender, EventArgs e)
{
MyRitchTextBox rtb = (sender as MyRitchTextBox);
if (rtb != null)
{
this.toolTip1.Show("Hello!!!", rtb);
}
}
void myRTB_MouseLeave(object sender, EventArgs e)
{
MyRitchTextBox rtb = (sender as MyRitchTextBox);
if (rtb != null)
{
this.toolTip1.Hide(rtb);
}
}
public class MyRitchTextBox : RichTextBox
{
}
}
This is not elegant, but you might be able to use the RichTextBox.GetCharIndexFromPosition method to return to you the index of the character that the mouse is currently over, and then use that index to figure out if it's over a link, hotspot, or any other special area. If it is, show your tooltip (and you'd probably want to pass the mouse coordinates into the tooltip's Show method, instead of just passing in the textbox, so that the tooltip can be positioned next to the link).
Example here:
http://msdn.microsoft.com/en-us/library/system.windows.forms.richtextbox.getcharindexfromposition(VS.80).aspx
Use:
ToolTip tip = new ToolTip();
private void richTextBox1_MouseMove(object sender, MouseEventArgs e)
{
Cursor a = System.Windows.Forms.Cursor.Current;
if (a == Cursors.Hand)
{
Point p = richTextBox1.Location;
tip.Show(
GetWord(richTextBox1.Text,
richTextBox1.GetCharIndexFromPosition(e.Location)),
this,
p.X + e.X,
p.Y + e.Y + 32,
1000);
}
}
Use the GetWord function from my other answer to get the hovered word.
Use timer logic to disable reshow the tooltip as in prev. example.
In this example right above, the tool tip shows the hovered word by checking the mouse pointer.
If this answer is still not what you are looking fo, please specify the condition that characterizes the word you want to use tooltip on.
If you want it for bolded word, please tell me.
I would also like to add something here that if you load desired form that contain tooltip controll before the program's run then tool tip control on that form will not work as described below...
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
objfrmmain = new Frm_Main();
Showtop();//this is procedure in program.cs to load an other form, so if that contain's tool tip control then it will not work
Application.Run(objfrmmain);
}
so I solved this problem by puting following code in Fram_main_load event procedure like this
private void Frm_Main_Load(object sender, EventArgs e)
{
Program.Showtop();
}
If you are using RichTextBox control. You can simply define the ToolTip object and show the tool-tip as the text is selected by moving the mouse inside the RichTextBox control.
ToolTip m_ttInput = new ToolTip(); // define as member variable
private void rtbInput_SelectionChanged(object sender, EventArgs e)
{
if (rtbInput.SelectedText.Length > 0)
{
m_ttInput.Show(rtbInput.SelectedText.Length.ToString(), rtbInput, 1000);
}
}
For the sake of ease of use and understandability.
You can simply put a Tooltip anywhere on your form (from toolbox). You will then be given an options in the Properties of everything else in your form to determine what is displayed in that Tooltip (it reads something like "ToolTip on toolTip1"). Anytime you hover on an object, the text in that property will be displayed as a tooltip.
This does not cover custom on-the-fly tooltips like the original question is asking for. But I am leaving this here for others that do not need
As there is nothing in this question (but its age) that requires a solution in Windows.Forms, here is a way to do this in WPF in code-behind.
TextBlock tb = new TextBlock();
tb.Inlines.Add(new Run("Background indicates packet repeat status:"));
tb.Inlines.Add(new LineBreak());
tb.Inlines.Add(new LineBreak());
Run r = new Run("White");
r.Background = Brushes.White;
r.ToolTip = "This word has a White background";
tb.Inlines.Add(r);
tb.Inlines.Add(new Run("\t= Identical Packet received at this time."));
tb.Inlines.Add(new LineBreak());
r = new Run("SkyBlue");
r.ToolTip = "This word has a SkyBlue background";
r.Background = new SolidColorBrush(Colors.SkyBlue);
tb.Inlines.Add(r);
tb.Inlines.Add(new Run("\t= Original Packet received at this time."));
myControl.Content = tb;
Related
I have WPF form with Grid and inside that Grid in Row(1).Column(1) i have StackPanel.
Inside that StackPanel i want to generate buttons.
I don't know how many buttons will be generated, since form(with grid and stackPanel) can be of different size.
Code below works, buttons are getting generated if i run that piece of code on Button_Click for example.
But buttons are not generated if I run this piece of code after InitializeComponent().
I guess, that after InitializeComponent WPF form is still not drawn(or finished loading) so my stPanel.ActualHeigh =="0", and since I can't divide with zero nothing acctualy happens.
Can you suggest some workaround, or even better proper solution?
public partial class frmReceipt : Window
{
public frmReceipt()
{
InitializeComponent();
addButtonGrp(); //am i too fast :)
}
private void addButtonGrp()
{
//Calculate size of container to determine numbers of button
int btnMinimumHeightSize = 30;
int btnNumberCreated = (Convert.ToInt16(stPanel.ActualHeight) / btnMinimumHeightSize);
for (int i = 0; i < btnNumberCreated; i++)
{
CreateGroupButtons btn = new CreateGroupButtons();
var btnX = new Button();
btnX = (btn.addButton(i, btnMinimumHeightSize, Convert.ToInt16(stPanel.ActualWidth)));
btnX.Click += ClickHandlerGrp;
if (i == btnNumberCreated - 1)
{
btnX.Height = btnMinimumHeightSize + ((Convert.ToDouble(stPanel.ActualHeight) / btnMinimumHeightSize) % 1) * (btnNumberCreated);
}
stPanel.Children.Add(btnX);
}
}
private void ClickHandlerGrp(object sender, RoutedEventArgs e)
{
var button = sender as Button;
MessageBox.Show("Clicked button number: " + button.Tag);
string test = Convert.ToString(button.Tag);
switch (test)
{
case "PLUGrp":
addButtonGrp(); //this is just for test, i don't want to generate buttons this way
break;
default:
break;
}
}
}
}
Thanks a lot!
I think you're right about running your code before the form has displayed. (It would be easy to check by putting a breakpoint on the for loop)
You can use the Loaded event of the form. Put this in your XAML for the window
Loaded="MainWindowView_OnLoaded"
and this in your C#
private void MainWindowView_OnLoaded(object sender, RoutedEventArgs e)
{
addButtonGrp();
}
This should then fire after the form is displayed, when you know the height of your stack panel.
I'm creating a simple game where a melody is played, and buttons that corresponds to notes are supposed to be highlighted.
Then the user is to push the button, and during each click the buttons are to highlighted again. I would like to place all the buttons in the GUI graphically.
Can I add a highLight method to the buttons in the GUI? I know I probably could create a new class that ineherits from some button class and create the buttons in the code but I would prefer to do it graphically.
What is the neatest way to handle the button outputs? I know I could paste in code for each button like
private void button_withIndexA(object sender, EventArgs e)
{
checkIfThisNoteWasCorrect();
highLightThisButton();
setHighLightOffForAllOtherButtons();
}
However, I think it would be neater to collect all buttons in some sort of container class and make a function like
class buttonArrayHandler
{
/*constructors etc*/
private void someButtonWasClicked(object sender)
{
/*Check which button was clicked, and do stuff accordingly*/
}
}
However I don't know how to do that. Suggestions?
You could change the button colors to create a highlight effect, but if it's a game you can use images / graphics for buttons, and swap them to another graphic when clicked.
When you double click on a button / graphic / control, by default it creates a method and links it to the click action for you. Instead click on each control, then the little lightning icon, and under the click action, pick the same method for all of them.
Then in your method cast the object sender to get the original control, for example:
var clickedButton = (Button)sender;
Where (Button) may be (Graphics) or whatever type of control you used as a button.
EDIT:
If you need to access a group of controls, you can either keep a global list of names at the top of the form and loop through them:
public List<string> buttonList = new List<string>() { "button1", "button2" };
void SomeMethod()
{
foreach (var controlName in buttonList)
{
this.Controls[controlName].Text = "TEST";
}
}
Or use a fixed name and number range:
void SomeMethod()
{
for (int i = 1; i <= 2; i++)
{
this.Controls["button" + i].Text = "TEST";
}
}
I would recommend adding the buttons programatically just like Carlos487 mentioned, here is the code segment that I made and could work to your advantage:
public Form1()
{
InitializeComponent();
int topMod = 0;
for (int i = 0; i < 5; i++)
{
MakeButton(i,topMod);
topMod += 20;
}
}
public void MakeButton(int index, int margin)
{
Button currentButton = new Button();
currentButton.Text = "Note" + index;
currentButton.Top += margin;
currentButton.Click += OnButtonClick;
panel1.Controls.Add(currentButton);
}
public void OnButtonClick(object sender, EventArgs e)
{
//checkIfThisNoteWasCorrect();
//highLightThisButton();
//setHighLightOffForAllOtherButtons();
MessageBox.Show("My Action was activated!");
}
As you can see you can place the common functions within the OnButtonClick method like I did here, giving all of the buttons the same event sequence. If you do not want to go through the whole process of programming buttons then you could also just do this:
currentButton.Click += OnButtonClick;
I have a WindowsForm application that has a FlowLayoutPanel container with a TextBox inside.
This control is bigger than the flow panel, I've set the flow panel to AutoScroll = true.
The problem is I don't know how to make the flow panel scroll to the position of the text edition. If I write continuously in the textbox eventually I pass beyond of what it is visible. The scroll remains at the top and I can't see what it is written.
In consequence the question is, how can I make the container react to keep visible what it is being written?
I think I finally did it:
public partial class Form1 : Form
{
public Point InitialTextBoxLoc ;
public Form1()
{
InitializeComponent();
InitialTextBoxLoc = textBox1.Location;
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
Point caretLocalLoc = textBox1.GetPositionFromCharIndex(textBox1.Text.Length-1);
Point caretLoc = new Point(caretLocalLoc.X + InitialTextBoxLoc.X,
caretLocalLoc.Y + InitialTextBoxLoc.Y);
Point scrollLoc = flowLayoutPanel1.AutoScrollPosition;
if (caretLoc.X >= flowLayoutPanel1.Size.Width-10)
{
scrollLoc.X = caretLoc.X;
}
if (caretLoc.Y >= flowLayoutPanel1.Size.Height-10)
{
scrollLoc.Y = caretLoc.Y;
}
flowLayoutPanel1.AutoScrollPosition = scrollLoc;
}
}
There is a hacky solution, but if there is nothing better you can try it. First, subscribe for a TextChanged event. Than, on text changed, check in which line is the caret, check what's the height of the line and scroll the flow layout panel to the position of the line.
The hacky bit is basically to get the height of the line. To do that you have to subtract Y coordinate of the second line from the Y coordinate of the first line.
So the code is:
private void textBox1_TextChanged(object sender, EventArgs e)
{
int lineHeight = 0;
if (textBox1.Lines.Count() > 1)
{
Point p1 = textBox1.GetPositionFromCharIndex(textBox1.GetFirstCharIndexFromLine(0));
Point p2 = textBox1.GetPositionFromCharIndex(textBox1.GetFirstCharIndexFromLine(1));
lineHeight = Math.Abs(p1.Y - p2.Y);
}
int lineIndex = textBox1.GetLineFromCharIndex(textBox1.SelectionStart);
flowLayoutPanel1.AutoScrollPosition = new Point(0, lineIndex * lineHeight);
}
I have a Tooltip with the ShowAlways property set to true.
On the controls where I want the tooltip to display (LinkLabels in this instance), I see there is a "ToolTip on <name of my Tooltip>" property which expects a string.
However, my tooltip is shared between 5 LinkLabels, and should differ depending on which one is hovered over.
I do have a shared click event that works:
private void linkLabelPlatypus1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
int Platypus = 1;
LinkLabel ll = null;
if (sender is LinkLabel)
{
ll = sender as LinkLabel;
}
if (null != ll)
{
if (ll.Name == linkLabelPlatypus2.Name)
{
Platypus = 2;
} else if (ll.Name == linkLabelPlatypus3.Name)
{
Platypus = 3;
} else if (ll.Name == linkLabelPlatypus4.Name)
{
Platypus = 4;
} else if (ll.Name == linkLabelPlatypus5.Name)
{
Platypus = 5;
}
toolTipPlatypi.SetToolTip(ll, DuckbillData.GetPlatypusDataForToolTip(Platypus));
}
}
...but I want the tooltips to also show on hover, and not require the user to click the label.
You only need to set the tooltip once :
public Form1()
{
InitializeComponent();
toolTip1.SetToolTip(linkLabel1, "foo");
toolTip1.SetToolTip(linkLabel2, "bar");
}
Done.
Doing this in a MouseHover or MouseEnter handler will call this function over and over each time the event fires. It will work, but it is unnecessarily complicated.
You only need one ToolTip on a form to provide tips for any number of components and it can provide them all simultaneously and continuously (ie: you don't have to change it or set it each time). Each component can have only one tip, but you can change it throughout the program any time you like. ShowAlways does not have to be true - it is used to make tooltips show on forms which are not active (ie: hover over an inactive window behind one with focus, etc).
You should write a event handler for Mouse Hover and have your tool tip display logic inside it.
private void Label1_MouseHover(object sender, System.EventArgs e)
{
//display logic
}
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.mousehover.aspx
I am building a WinForms program that connects to a DB. On one form I want to display a list of elements recovered from the DB. The elements have to be clickable (radio buttons are an option here), and must have a hover option, as I want some info to appear in a textbox when mouse is hovered over a specific item.
I cannot find an adequate ToolBox control for this. Has anyone got some suggestions? I am using VS2010.
Thanks.
There is no such ready-to-use control in .net framework instead you have to design/create your own using Window custom controls.
Using a standard ListBox, you can just track the mouse position with the MouseMove event.
Example:
int _HoverIndex = -1;
private void listBox1_MouseMove(object sender, MouseEventArgs e) {
int index = listBox1.IndexFromPoint(e.Location);
if (index != _HoverIndex) {
_HoverIndex = index;
if (_HoverIndex == -1)
textBox1.Text = string.Empty;
else
textBox1.Text = listBox1.Items[_HoverIndex].ToString();
}
}
private void listBox1_MouseLeave(object sender, EventArgs e) {
_HoverIndex = -1;
textBox1.Text = string.Empty;
}