Label UserPaint drawn text doesn't fit

2019-08-16 14:28发布

问题:

I have a custom Label class, drawn text doesn't fits. What am I doing wrong here?

class MyLabel: Label
{
    public MyLabel()
    {
        SetStyle(ControlStyles.SupportsTransparentBackColor | ControlStyles.UserPaint, true);
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        using (LinearGradientBrush brush = new LinearGradientBrush(ClientRectangle, Color.Black, Color.LightGray, LinearGradientMode.ForwardDiagonal))
            e.Graphics.DrawString(Text, Font, brush, ClientRectangle);
    }
}

If I set text of MyLabel to be "123456790 123456790" (AutoSize = true), then I see in Designer (or at run-time) "1234567890 123456789 " (no last zero, but some space). If I try "1234567890 1234567890 1234567890 1234567890", then there will be "1234567890 1234567890 1234567890 12345678 " (no "90", but again some space).

回答1:

e.Graphics.DrawString(Text, Font, brush, ClientRectangle);

You are using the wrong text rendering method. The Label class auto-sizes itself based on the return value of TextRenderer.MeasureText(). You must therefore use TextRenderer.DrawText() to get the exact same rendered output. You can also set the label's UseCompatibleTextRendering property to true but that should not be your first choice.



回答2:

Use Graphics.MeasureString to get the required size of the bounding box, then set the size of the label's surface to that size.



回答3:

Here goes a solution (possible not the best one) to the described problem which can be rephrased as "Autosized Label with Graditent text color".

class MyLabel: Label
{
    private bool _autoSize = true;
    /// <summary>
    /// Get or set auto size
    /// </summary>
    public new bool AutoSize
    {
        get { return _autoSize; }
        set
        {
            _autoSize = value;
            Invalidate();
        }
    }

    public MyLabel()
    {
        SetStyle(ControlStyles.SupportsTransparentBackColor | ControlStyles.UserPaint, true);
        base.AutoSize = false;
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        // auto size
        if (_autoSize)
        {
            SizeF size = e.Graphics.MeasureString(Text, Font);
            if (ClientSize.Width < (int)size.Width + 1 || ClientSize.Width > (int)size.Width + 1 ||
            ClientSize.Height < (int)size.Height + 1 || ClientSize.Height > (int)size.Height + 1)
            {
                // need resizing
                ClientSize = new Size((int)size.Width + 1, (int)size.Height + 1);
                return;
            }
        }
        using (LinearGradientBrush brush = new LinearGradientBrush(ClientRectangle, Color.Black, Color.LightGray, LinearGradientMode.ForwardDiagonal))
        e.Graphics.DrawString(Text, Font, brush, ClientRectangle);
    }
}

Idea behind is very simple: override AutoSize and process it within Paint event (everything in one place), if required size of text is different from ClientSize - resize control (which will cause redraw). One thing is what you have to add +1 to the width and height because SizeF have fractions and it's better to have +1 pixel more than loose 1 pixel sometimes and have your text not fitting.