2

i'm working on this small WinForm app and decided to use custom drawn tooltip with semi-transparent background. So i started with setting the OwnerDraw property on ToolTip to True, created event handlers for Draw and Popup events (see the example code bellow. The commented version isn't working either).

private void toolTip_Popup(object sender, PopupEventArgs e)
{
    e.ToolTipSize = new Size(400, 400);
}

private void toolTip_Draw(object sender, DrawToolTipEventArgs e)
{
    //e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(120, Color.Red)), new Rectangle(e.Bounds.Location, e.Bounds.Size));
    e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(120, Color.Red)), new Rectangle(0, 0, 400, 400));
}

Now when the ToolTip is shown for the first time for a specific control everything works as intended. See the picture bellow (400x400 tooltip with semi-transparent red background).

enter image description here

But when i hover over the same control for the second time ToolTip loses its semi-transparency. See the picture bellow. Why is that so?

enter image description here

eren
  • 708
  • 8
  • 20
  • 1
    ToolTip cannot support transparency effects by itself. You can fake it, make a screenshot in Popup with Graphics.CopyFromScreen(). In Draw then paint that bitmap first so it looks transparent and paint the tip on top of it. Your app needs to be dpiAware to bring this to a good end on any monitor. – Hans Passant Jul 26 '18 at 13:10
  • 1
    Ok but why it can show the transparent background the first time it is used for a specific control? – eren Jul 26 '18 at 13:12
  • @HansPassant i'm not sure if you understood my question. The pictures are screenshots from WinForm app (they are not made up in some SW like this is the effect that i want). The semi-transparent background for TooTtip is working, the problem is that it shows that semi-transparent background only once (the first time ToolTip is shown for a control). – eren Jul 26 '18 at 13:58
  • 1
    It doesn't loses its semi-transparency it draws a black background underneath your semi red. If you see in your second image the color isn't red but deep red. – γηράσκω δ' αεί πολλά διδασκόμε Jul 26 '18 at 14:34
  • About the initial transparency, try testing it with `ToolTip.UseFading = false`. – Jimi Jul 26 '18 at 15:16
  • @Jimi Nope that didn't help, going to take a look @ Ben Voigts answer now – eren Jul 26 '18 at 15:51
  • It wasn't supposed to help. But to show something. You don't have a real control over what the ToolTip class actually paint on the screen. ToolTip is, primarily, a `System.ComponentModel.IExtenderProvider` class. Doesn't support `.SetStyle`, you can't override an `OnPaint` method, because it doesn't have one. It's not what you see on screen, it's the handler. The initial transparency is caused by the fading effect that you abort. The subsequent layer is buffered. That's what you see after. – Jimi Jul 26 '18 at 15:55
  • 1
    What you could do, is to build a class that derives from `ToolTip` (to exploit it's mouse-driven/timer-driven event system, abort the tooltip on the `Popup` event (that you have to subscribe, you can't override it), and show a translucent label (or a similar control) in the poistion you can derive from the provided `e.AssociatedControl` and move the substitute control in a calculated location of the `e.AssociatedControl` `.Bounds`. Other paths are, IMO, frustrating. – Jimi Jul 26 '18 at 16:07
  • @Jimi I'll take look at it, thank you very much for your input :) – eren Jul 26 '18 at 16:08
  • @HansPassant Thanks for the help. So far i ended up using Graphics.CopyFromScreen(), it is working +- good even within different DPI scaling settings(with some help from https://stackoverflow.com/questions/5977445/how-to-get-windows-display-settings). I may update my answer if i utilise a better solution. – eren Jul 27 '18 at 10:34

3 Answers3

4

Thank you all for your help. I'm pretty sure that Ben Voigts answer, or Jimis comments could solve this problem somehow too (i'll try them out later and update the answer if i'll be able to utilise them).

I based my solution on the first comment made by Hans Passant where he suggested to use Graphics.CopyFromScreen() in Popup event handler(toolTipDay_Popup), to capture the image underneath the ToolTip and then in Draw event handler(toolTipDay_Draw) i just drew the captured image.

(There is a problem with different DPI scalings as noted by Hans Passant, but that can be +- solved by Farshid T answer in How to get Windows Display settings?, i didn't include it in code bellow).

So the solution i'm using right now is as follows:

Bitmap dayToolTipBackground = new Bitmap(200, 200);
private void toolTipDay_Popup(object sender, PopupEventArgs e)
{
     e.ToolTipSize = new Size(200, 200);
     var backGraphics = Graphics.FromImage(dayToolTipBackground);
     var cursorPosition = Cursor.Position;
     backGraphics.CopyFromScreen(new Point(Cursor.Position.X, Cursor.Position.Y + 21), new Point(0, 0), new Size((200, 200)));    
}


private void toolTipDay_Draw(object sender, DrawToolTipEventArgs e)
{
     e.Graphics.DrawImage(dayToolTipBackground, new Point(0, 0));
     e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(120, Color.Red)), new Rectangle(e.Bounds.Location, e.Bounds.Size));
}
eren
  • 708
  • 8
  • 20
0

The first time you hover over the control, a new instance of tooltip is created with the transparent color (ARGB.120). When you click outside the tooltip, whether the tooltip object is fully disposed or are you simply setting the instance as invisible?

When you are hovering over the instance for the second time, then, if the old tooltip object is not disposed, there is a chance that the same object is called again. So now when you do Graphics.FillRectangle() on an existing tooltip with color = ARGB.120, you are just overlaying another layer of ARGB.120 color on it, which will darken it further because the color levels are changed.

Therefore when you click outside the tooltip after you call it for the first time, you might need to dispose the tooltip object(or the e.Graphics object, if that doesn't affect other parts of your application) and create new tooltip objects every time you hover over the control.

AKK
  • 35
  • 5
-1

Windows needs to be told that the windows beneath the popup need to be redrawn. This is done via a "layered window" style. With layering, the content gets drawn in z-order and transparency blending works. Without layering, only the top window gets sent a repaint and it draws on top of meaningless leftover data in the DC's screen buffer.

You can try p/invoking SetLayeredWindowAttributes

I strongly recommend reading the MSDN documentation on Layered Windows: Here and here

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720