Anchoring a Windows Forms Tooltip to the mouse - c#

I want to show a tooltip when hovering over a button and as long as the mouse is over the button the tooltip should follow the mouse. What is the correct way to achieve that?
When I add a MouseMove event that calls tooltip.Show(...) with the updated mouse position it flickers extremely, and also redraws the tooltip when the mouse rests. And if it is an OwnerDraw tooltip I can see the default system tooltip style "fighting" with the self-drawn tooltip.

Indeed, with .Net 2.0 the ToolTip object has been altered. Before 2.0, there were some inconsistency problems when the ToolTip text was changed while the ToolTip was active, or with some other situations.
Since 2.0, the Tooltip is hidden each time something happens what could affect the currently active Tooltip.
While this solved some problems, it now causes some events being fired right after e.g. a SetToolTip(), even if this function has been called from within this very event, resulting in an endless loop of ToolTip draw/hide until the mouse moves away from the ToolTip area.
My own workaround is to check whether the ToolTip is already the same and omitting the Set ToolTip() if so. (simply omitting the next event by a static flag as suggested above can cause problems as there is no guarantee that there will be a new event right after, e.g. if the mouse has just touched the ToolTip area and moved away already).
Also, using OnMouseHover just to display a Tooltip disables the internal timer functionality of the ToolTip component as well as causing many many unnecessary events and therefore wastes processor time. The Popup Event of the ToolTip component serves well as point of action.
In this special case, however, OnMouse Hover is necessary to track the mouse movement.
Anyways, altering the ToolTip position causes a complete redraw of the Tooltip and therefore flicker. This can be reduced for a motionless mouse by checking whether the mouse position has changed between two events.
Unfortunately, the ToolTip component has no way to change the position of the ToolTip adn is shown always relative to the current mouse position. So the only way to have it follow the mouse is to close and redraw it.
it MAY help to set the UseFading and/or UseAnimation properties to false so the flicker can be further reduced.

OK, this may be complete overkill, and probably not the best solution, but I think a fun little hack nonthless.
Basically, I'm drawing a ListView at the location of the mouse. Some code:
ListView v = new ListView();
public Form1()
{
InitializeComponent();
v.Items.Add("Foo");
v.Height = 30;
v.Width = 50;
this.button1.Controls.Add(v);
v.MouseMove += new MouseEventHandler(v_MouseMove);
v.BackColor = SystemColors.Info;
this.button1.MouseMove += new MouseEventHandler(button1_MouseMove);
}
void v_MouseMove(object sender, MouseEventArgs e)
{
v.Location = new Point(v.Location.X + e.Location.X, v.Location.Y + e.Location.Y);
}
void button1_MouseMove(object sender, MouseEventArgs e)
{
v.Location = e.Location;
}

I've noticed that when manually showing a tooltip with OnMouseHover, OnMouseMove gets called one more time after the tooltip is shown. As a hack, I've ignored the next OnMouseMove call immediately following the tooltip being shown (using a flag). Perhaps a similar phenomenon is occurring?

Related

Sensitive border area between MouseEnter and MouseLeave events

I have a PictureBox control on my form for which I have written two events for MouseEnter and MouseLeave. On MouseEnter anther PictureBox enters the form and stands beside the original and with MouseLeave the second PictureBox goes way.
All works fine. Except when the cursor is on the original PictureBox’s border area the MouseEnter and MouseLeave events are repeatedly run. So the second image enters and leaves the form until the cursor is taken away. This makes a strange sight.
How can I avoid this situation?
The border area can be tricky, especially when you want to trigger something the might influence it even if it is only by a few pixels..
One classic situation is when you want to resize or move a control by clicking an dragging it at its border. Unless you use the internal calls and simply code mouseenter, -leave, -move, -down and -up you may well end up with e.g. moving the control away from the mouse and thereby triggering another leave event.
This often occurs only at one set of borders, like left&top or right&bottom.
You need to check you code for any such influences, like the new PictureBox pushing the old one away by a few pixels or a resize that makes it smaller; even one pixel can lead to the effect you see..
If the MouseEnter event is triggering a border to be drawn, or the size of the first PictureBox to change in any way, that can cause the effect you describe.
You could add a check against the mouse coordinates in MouseEnter to ensure that the mouse pointer gets far enough into the control's interior before the event fires. That would prevent an instant firing of the Leave event.

The mouse event GetItemAt(e.X, e.Y) gives wrong values for ListView control

Strange behavior with C# (VS 2010) ListView Control mouse click event (items are without checkbox):
private void m_ListView_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
// Returning some times wrong values
ListViewItem lvi = m_ListView.GetItemAt(e.X, e.Y);
//.... more logic here
}
}
After I clicked the item, and immediately I moved the mouse down or up, some other item is getting selected instead of what is intended;
I debugged and came to know that e.X, e.Y is capturing WRONG values. When I click and leave the mouse pointer unmoved, it's working fine. But if I clicked and immediately moved to another row, one of that row is getting selected instead.
Questions:
Is this a known issue with ListView mouse events?
Is there any other way to retrieve correct coordinates for the mouse events?
This is an enterprise level project, where performance is very important. We have written original code for all mouse events like up, down, doubleclick, mouseclick, itemcheck etc.
-Karthik
ListViewItem lvi = m_ListView.FocusedItem;
That should work to get the item that was just clicked (Focused)
m_ListView.GetItemAt(e.X, e.Y);
Would get the item at the mouse location, which is why it changes when you move the mouse quickly away, if you want the item that you just clicked, m_ListView.FocusedItem should work fine
You may want to consider what "wrong value" constitutes. The click-event is a high-level event and the moment the code is executed is not necessarily equal to where the mouse is at that time. The stored coordinates of that click event are from the moment the user clicked the button. If you continue to move the mouse, the coordinates inside the event will be unequal to the coordinates of the mouse at that same moment.
You say that you handle all mouse events. If it is for performance, consider using the build-in mechanisms of the control, these are optimized for speed and accuracy. It's hard to improve on 10 years of development (the control, through various earlier incarnations, has seen quite some development time at MS).
If you really need the utmost speed, write your own control instead from scratch. Use low-level mouse events to get the best available information.
This is by design, the MouseClick event is generated by the MouseUp event. All click events work like that. Pushing the button, moving the mouse and releasing the button thus always gives the location where you released the button, not where you pushed it.
You would need to use the MouseDown event instead.

Removing Windows' ugly Selection marker thing from Splitter in SpitContainer Control

I have a SplitContainer control, and the Splitter in the middle is very ugly. By setting the BackColor of the SplitContainer to (insert color here), then setting the BackColor of Panel1 and Panel2 to white, I can have my splitter looking nice. But by default, Windows puts the selection mark over the Splitter, even before it's selected.
How can I make sure that the selection mark never shows on the Splitter?
I think by "Selection Marker Crap", you mean the fuzzy line that indicates the control is selected. If you don't want this showing up, set some other control to be selected at startup. Something like:
Textbox1.Selected = true;
This should solve your issue if it is just one of it not being selected. However, this will come back if you select the item to resize something. In that case, you could put something in the mouse_up event to move the selection off of the control. That way, the user moves the splitter bar and then when they let go, the selection gets cleared off of the splitter.
Another way would be to make the splitter bar narrow enough that the gray fuzzy line doesn't show up. To do this, you could do the following (tested):
splitContainer1.BorderStyle = BorderStyle.FixedSingle;
splitContainer1.SplitterWidth = 1;
I experienced the same problem, and fixed it by setting TabStop to False in the Properties window for SplitContainer1.
This could annoy people who depend or insist on using the keyboard to operate every aspect of your form, but other than that it will work. Controls inside the SplitContainer will remain tab-able, just not the SplitContainer itself.
This code will move the focus from the splitContainer to TreeView shortly after moved.
private void splitContainer1_SplitterMoved(object sender, SplitterEventArgs e) {
if(this.splitContainer1.CanFocus) {
this.splitContainer1.ActiveControl = this.treeView1;
}
}
You could add an event handler to steal the focus from the container on MouseUp's... It's a little messy but it works. :)
I tried a lot to remove splitter but nothing work. I did some different why we need to use splitter for that we can use picture box control make it width (or) height depend upon your project set 5 or 3 .... after picture box mouse move event write code like...
picturebox property-cursor change the cursor type Hsplit its look like splitter
private void picturebox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)//this for mouse left click its work
{
//write you code here if you use panel set panel height or width it reaches...
Cursor.Position = new Point(e.X, e.Y); // this for mouse cursor position according your //project do some fine tune you will get its work...
}
its work because i tried lot for this and i itself found this method...
I set the TabStop to false and it went away.
Most simple solution i found/made - create a button, select it, and hide it.
All via code. there is not side effects or problems with this, place it in the forms load event.
Button DeSelectButton = new Button();
this.Controls.Add(DeSelectButton);
DeSelectButton.Select();
DeSelectButton.Visible = false;

Problem with MouseMove event, launched when the content of control beneath is moved with arrow keys

My UserControl is a kind of a container that has a set of controls inside. One of the UI behaviours I designed is that the move over nested control makes it selected for some keyboard triggered actions.
The other way of setting up the nested control that is targeted to receive keyboard input is with arrow keys that change focus to certain control within my UserControl.
The problem is that in most cases my UserControl has scrollbars and switching between its elements with arrow keys causes contents to move. Because of that movement it seems that mousemove event is launched also when the mouse arrow stands still, but its over my usercontrol. In the end, the wrong nested control is being selected.
I tried to set a boolean flag to temporary lock the actions of mousemove event while the arrow keys handling function is launched, but it seems to not to work at all.
Does anybody have an idea how to prevent that unwanted triggering of mousemove event and avoid the problems it causes?
I did some debugging/experiments and it appears that though mouse stays still over controll the MouseMove is actually called frequently and not only when control move is caused by some UI behaviour (scrolling of parent controll called using keyboard).
It also seems like it is called twice every single second (but probably only when application is launched form VS during debug session?), the calls are separated by few hundred parts of seconds and for now i couldnt determine what causes those.
Solution (maybe not only, and not the best) is to store last position of cursor and compare it inside event to check if mouse was really moved from last MouseMove call. I achieved it using System.Windows.Forms.Cursor.Position because position from MouseEventArgs will be diffrent depending on circumstances but still will be inconclusive to determine if mouse cursor was moved.
Point lastCursorPosition = new Point();
private void panelPictures_MouseMove(object sender, MouseEventArgs e)
{
if (System.Windows.Forms.Cursor.Position != lastCursorPosition)
{
Console.WriteLine("mouse moved");
lastCursorPosition = System.Windows.Forms.Cursor.Position;
}
else
{
Console.WriteLine("mouse in place");
}
}

Canvas Click events in Silverlight when not over a UIElement

I have a canvas object and I am sprinkling fantastic controls all over it. I'm using a ScaleTransform object to scale the canvas so I can zoom in/out.
I have wired up the controls so that I can drag them around, and the drag and drop works well by using MouseLeftButtonDown, MouseLeftButtonUp, and MouseMove. Now, I want to work on enabling an event when I click only on the Canvas. When I read the docs for the canvas object, I see that the MouseLeftButtonDown event only fires when it's over a UIElement.
Occurs when the left mouse button is
pressed (or when the tip of the stylus
touches the tablet PC) while the mouse
pointer is over a UIElement.
(Inherited from UIElement.)
Unfortunately, I want the opposite behavior. I want to know when the mouse is clicked on the Canvas while the mouse pounter isn't over any controls. Since I'm new to Silverlight, I could be doing this the wrong way. Is there something I have overlooked? Am I going about this the wrong way? I'm looking for a little help, and perhaps a lot of direction.
I'm no Silverlight guru, but could you add a transparent UIElement to the Canvas, below all other UIElements, and use it to determine if the user has clicked outside of any of the other drag/drop-able elements.
You want to know when a click happens on the Canvas and isn't over other controls?
The most natural thing is to capture the Canvas's MouseLeftButtonDown. Inside of that event, take a peak to see where the click happened. Then peak at the UIElements under the click. I recommend you keep everything in absolute coordinates to keep things straight. Something like:
void Page_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Point p = e.GetPosition(null);
var elements = VisualTreeHelper.FindElementsInHostCoordinates(p, App.Current.RootVisual);
foreach (var element in elements)
{
//Figure out if you're over a particular UI element
}
}
I think you may be interpreting the documentation wrong. According to MSDN, Canvas itself is an implementation of a UIElement:
System.Windows.UIElement
System.Windows.FrameworkElement
System.Windows.Controls.Panel
System.Windows.Controls.Canvas
From my experience, and correct me if I'm wrong, MouseLeftButtonDown usually only fires for the top-most UIElement clicked. So if you implement MouseLeftButtonDown for your Canvas, it should only fire when the Canvas is clicked, and NOT when the buttons are clicked. I'd say try it out first.
In WPF, I think this would be easily solved by routed events. However, Silverlight didn't get this feature. You may want to check out VisualTreeHelper.FindElementsInHostCoordinates. This article covers it a little bit.
http://www.andybeaulieu.com/Default.aspx?tabid=67&EntryID=95

Categories

Resources