2

I'm building custom control that will display tiles, like colored grid. I've managed to do drawing, scrolling and basic logic, but I have problem with creating tooltip for each tile.
Each tile color depends on data that is "bound" to that tile.

enter image description here

I'll try to describe my idea:
Above image shows my control, I have 4 squares drawn there, I'd like to show different tooltip when user hovers different parts of my control. Below is my tooltip (small red rectangle) and lower is standard WinForms Chart component.
I'd like to get this kind of behavior in my control (tooltip is outside of control, so long text is displayed properly).

enter image description here

Below is code of my control with just basic functionality:

public sealed class UC1 : UserControl
{
    private bool _showTooltip;
    private string _tooltipText;
    private Point _mousePosition;

    public UC1()
    {
        MinimumSize = new Size(100, 100);
        Size = new Size(100, 100);
        SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.ResizeRedraw | ControlStyles.OptimizedDoubleBuffer, true);
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        e.Graphics.FillRectangle(Brushes.LightGoldenrodYellow, 0, 0, Width/2, Height/2);
        e.Graphics.FillRectangle(Brushes.LightGray, Width/2, 0, Width, Height/2);
        e.Graphics.FillRectangle(Brushes.LightSlateGray, 0, Height/2, Width/2, Height);
        e.Graphics.FillRectangle(Brushes.LightSteelBlue, Width/2, Height/2, Width, Height);

        using (var p = new Pen(Color.Black, 2))
        {
            e.Graphics.DrawLine(p, Width/2, 0, Width/2, Height);
            e.Graphics.DrawLine(p, 0, Height/2, Width, Height/2);
        }

        if (_showTooltip)
        {
            SizeF c = e.Graphics.MeasureString(_tooltipText, DefaultFont);
            int width = (int) c.Width;
            int height = (int) c.Height;
            const int offset = 12;

            var x = _mousePosition.X + width + offset > Width ? _mousePosition.X - width : _mousePosition.X + offset;
            var y = _mousePosition.Y + height + offset > Height ? _mousePosition.Y - height : _mousePosition.Y + offset;

            e.Graphics.FillRectangle(Brushes.Red, x, y, width, height);
            e.Graphics.DrawString(_tooltipText, DefaultFont, Brushes.Black, x, y);
        }
    }

    protected override void OnMouseMove(MouseEventArgs e)
    {
        base.OnMouseMove(e);
        if (e.X > 0 && e.X < Width/2 && e.Y > 0 && e.Y < Height/2)
        {
            Debug.WriteLine("1,1 square");
            _tooltipText = "1,1";
        }
        else if (e.X > Width/2 && e.X < Width && e.Y > 0 && e.Y < Height/2)
        {
            Debug.WriteLine("1,2 square");
            _tooltipText = "1,2";
        }
        else if (e.X > 0 && e.X < Width/2 && e.Y > Height/2 && e.Y < Height)
        {
            Debug.WriteLine("2,1 square");
            _tooltipText = "2,1";
        }
        else if (e.X > Width/2 && e.X < Width && e.Y > Height/2 && e.Y < Height)
        {
            Debug.WriteLine("2,2 square");
            _tooltipText = "2,2";
        }
        _mousePosition = e.Location;
        _showTooltip = true;
        Refresh();
    }

    protected override void OnMouseLeave(EventArgs e)
    {
        _showTooltip = false;
        Refresh();
        base.OnMouseLeave(e);
    }
}

I've found similar question: How to make a floating (tooltip) control in Windows.Forms? but I'd like to avoid creating custom tooltip and instead use standard one.

How can I show standard tooltip that will change it text when hovering on different tiles. I'd like to add everything to my user control, so I won't have to add any code to form hosting my control.

Community
  • 1
  • 1
Misiu
  • 4,738
  • 21
  • 94
  • 198
  • If you want to display text outside of the bounds of a window then you must use a toplevel window. You won't find a lot of them in the toolbox, ToolTip is it. The Form class is toplevel. Making it small enough requires whacking the Size in the Load event, ensuring it stays on top requires the Show(owner) overload. – Hans Passant Dec 12 '15 at 09:07

1 Answers1

2

Why do you try to draw the ToolTip yourself instead of using the system one?

Just add one to the UC class

// private bool _showTooltip;      ?? probably not needed any more..
private string _tooltipText;   
// private Point _mousePosition;   ??..
ToolTip ttip = new ToolTip();

and set it like this:

// _mousePosition = e.Location; ??..
// _showTooltip = true;         ??..

ttip.SetToolTip(this, _tooltipText);  // use this in the mousemove
Refresh();

Of course now you can skip the whole Painting part..

If you want to control the location where the ToolTip is shown use one of the ShowToolTip() overloads instead of SetToolTip() ..!

While both are still there this is the result, going over the UC's border and displaying with a nice drop shadow..:

enter image description here

If you really want your ToolTip to look different from the usual ones, you can set its OwnerDraw to true and code its Draw event just like any other control with GDI+ graphics methods..

Update:

There is an inherent flicker problem; for an explanation see Hans' answer here; his recommendation #2 and one of the answers is helpful:

Remember last mouse position and set the tooltip only when the mouse position changes.

So we need to add a last ToolTip location:

  Point tipLoc = Point.Empty;

Which we test and set in the mouse move:

if (tipLoc != e.Location )
{
    tipLoc = e.Location;
    ttip.SetToolTip(this, _tooltipText);
}
Community
  • 1
  • 1
TaW
  • 53,122
  • 8
  • 69
  • 111
  • I'd tried to avoid adding tooltip to my control and I wanted to show that I've tried something before posting question on SO. I'll update my code. Do I must call `Show()` and `Hide()` on tooltip to hide it when I move mouse outside my control? And do I must dispose Tolltip in DIsposing of my control? – Misiu Dec 12 '15 at 09:00
  • You only create one and it will hide upone leaving by default. So nothing else is needed, imo. Show and Hide are only for enforcing a tooltip when somehow necessary; I don't think you need it; the moving will enforce it automatically. – TaW Dec 12 '15 at 09:01
  • thanks for clarifying :) I know I should create only one. I'll try that right away! – Misiu Dec 12 '15 at 09:02
  • Works almost perfect. I'm setting tooltip text inside `OnMouseMove`, tooltip is shown, but when I move mouse I get flickering effect on tooltip, probably because `SetToolTip` is refreshing tooltip. How can I remove flickering? – Misiu Dec 12 '15 at 09:09
  • 1
    I've got it working. I've created local variable inside `OnMouseMove` I'm calculating new text and only it it is different from previous one I'm calling `SetToolTip`. – Misiu Dec 12 '15 at 09:15
  • One more thing: Can I make tooltip follow cursor? – Misiu Dec 12 '15 at 09:15
  • It is following the cursor, but maybe you have stopped that when checking for the text changes? Maybe add a check for changed location? – TaW Dec 12 '15 at 09:16
  • Yes, that's the reason. Is there other way to remove flickering? No I have two options: no flickering and no follow or flickering and mouse follow. – Misiu Dec 12 '15 at 09:19
  • Hm, I find that the MouseMove gets called all the time event though it isn't moved. I still don't see why.. [this post](http://stackoverflow.com/questions/8777323/how-to-prevent-tooltip-from-flickering-in-custom-control) looks interesting.. – TaW Dec 12 '15 at 09:20
  • Hans recomendations 1&3 are not as helpful as his explantion; but the 3rd one or the simple answer from Petr does the job.. – TaW Dec 12 '15 at 09:35