2

I've sub-classed a control (ToolStripStatusLabel) to try and override the way it paints. At the moment I would expect this code to effectively do nothing, but it leads to a strange output:

protected override void OnPaint(PaintEventArgs e)
{
  // Create a temp image to draw to and then put that onto the control transparently
  using (Bitmap bmp = new Bitmap(this.Width, this.Height))
  {
    using (Graphics newGraphics = Graphics.FromImage(bmp))
    {
      // Paint the control to the temp graphics
      PaintEventArgs newEvent = new PaintEventArgs(newGraphics, e.ClipRectangle);
      base.OnPaint(newEvent);

      // Copy the temp image to the control
      e.Graphics.Clear(this.BackColor);
      e.Graphics.DrawImage(bmp, new Rectangle(0, 0, this.Width, this.Height), 0, 0, bmp.Width, bmp.Height, GraphicsUnit.Pixel);//, imgAttr);
    }
  }
}

When I run this code the text on the control comes out very strangely, the expected image is at the top, the actual output is at the bottom:

output

It looks like when the control is drawing the text the alpha blending with the antialiased text is going wrong.

Things that I've tried:

  • Setting the CompositingMode of e.graphics and newGraphics
  • Setting the TextRenderingHint.
  • setting the pixel format of newGraphics to 32Bpp ARGB and Premultiplied ARGB
  • Clearing the newGraphics with the controls background colour before asking the base class to render.
steeveeet
  • 639
  • 6
  • 25
  • Try `PixelFormat.Format32bppPArgb` for the `Bitmap`, or clear the bitmap instead of the control before drawing. If I'm not mistaken this is the kind of issue that requires premultiplied alpha. – Joey Jan 23 '18 at 15:56
  • I forgot to mention that I've tried explicitly setting the pixel format - I tried 32bppARGB, but changing it to 32bppPARGB didn't have any effect. I have also tried clearing the newGraphics context before getting the control to render – steeveeet Jan 23 '18 at 17:18
  • Sorry, the base class is just a ToolStripStatusLabel – steeveeet Jan 23 '18 at 17:47
  • @joey Re the background clearing: that certainly helps when using DrawString() (i've just done a test with that), but strangely it makes no difference to the results of the base class drawing. I wonder what Microsoft are doing in there! – steeveeet Jan 23 '18 at 17:49
  • If you are trying to customize the appearance of `ToolStripStatusLabel`, creating a renderer is the way to go. Take a look at [this post](https://stackoverflow.com/a/38155401/3110834) for example. – Reza Aghaei Jan 23 '18 at 18:09
  • Also you should know you can use a `Label` inside the `StatusStrip`. Take a look at [this post](https://stackoverflow.com/a/38359163/3110834) for example. – Reza Aghaei Jan 23 '18 at 18:11

1 Answers1

0

TL;DR: You'll need to render the text yourself with a custom renderer that re-implements OnRenderItemText, probably using graphics.DrawString() to do the drawing ultimately.

Another option would be to use a label in the status bar (as mentioned by @Reza Aghaei). Then you can set UseCompatibleTextRendering to true to make it use GDI+ rather than GDI


This seems to be an inherent problem in the way that the text is rendered at the lowest level. If you add a normal ToolStripStatusLabel and set its TextDirection to be Vertical90 then you get the same result, where the anti-aliasing of the text appears to have no alpha with the background.

Looking at the source you'll see that a very similar bit of code is called where the text is rendered to a bitmap and then in this case rotated:

            using (Bitmap textBmp = new Bitmap(textSize.Width, textSize.Height,PixelFormat.Format32bppPArgb)) {

                using (Graphics textGraphics = Graphics.FromImage(textBmp)) {
                    // now draw the text..
                    textGraphics.TextRenderingHint = TextRenderingHint.AntiAlias;
                    TextRenderer.DrawText(textGraphics, text, textFont, new Rectangle(Point.Empty, textSize), textColor, textFormat);
                    textBmp.RotateFlip((e.TextDirection == ToolStripTextDirection.Vertical90) ? RotateFlipType.Rotate90FlipNone :  RotateFlipType.Rotate270FlipNone);
                    g.DrawImage(textBmp, textRect);
                }
            }

So this seems like a fundamental problem when text is rendered to a bitmaps graphics context (as opposed to the control's graphics context). Ultimately the code that is called is:

        using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, flags ))
        {
            using (WindowsFont wf = WindowsGraphicsCacheManager.GetWindowsFont( font, fontQuality )) {
                wgr.WindowsGraphics.DrawText( text, wf, bounds, foreColor, GetIntTextFormatFlags( flags ) );
            }
        }

Which I think is wading off into GDI (as opposed to GDI+) which has trouble with alpha on text.

Your best bet is to write a custom renderer that re-implements OnRenderItemText, probably with some 'inspiration' from the source from the default implementation.

steeveeet
  • 639
  • 6
  • 25