c# - Adding missing properties support to ToolStripStatusLabel which overrides OnPaint? - Stack Overflow

admin2025-04-16  2

I am using the following class which inherits from ToolStripStatusLabel and adds the feature to ellipsis text which is too long to display in the control.

[ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.StatusStrip)]
    public partial class SpringLabel : ToolStripStatusLabel
    {
        public SpringLabel()
        {
            this.Spring = true;
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            var flags = TextFormatFlags.Left | TextFormatFlags.EndEllipsis | TextFormatFlags.VerticalCenter;
            var bounds = new Rectangle(0, 0, this.Bounds.Width, this.Bounds.Height);
            TextRenderer.DrawText(e.Graphics, this.Text, this.Font, bounds, this.ForeColor, flags);
        }
    }

This works fine but is missing support for various properties of the parent control, such as BorderSides as well as any support for images and so on. Is there a way to get at the ToolStripStatusLabel code to try and copy or emulate some of its behavior? I really have no ideas on where to start looking.

Edited to add the solution as suggested by @Hans

[ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.StatusStrip)]
    public partial class SpringLabel : ToolStripStatusLabel
    {
        private string? _sltext;
        public new string? Text
        {
            get => _sltext;
            set { _sltext = value; ToolTipText = _sltext; Invalidate(); }
        }
        public SpringLabel()
        {
            this.Spring = true;
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            var flags = TextFormatFlags.Left | TextFormatFlags.EndEllipsis | TextFormatFlags.VerticalCenter;
            var bounds = new Rectangle(0, 0, this.Bounds.Width, this.Bounds.Height);
            TextRenderer.DrawText(e.Graphics, _sltext, this.Font, bounds, this.ForeColor, flags);
        }
    }

This works just fine but likely doesn't support some of the properties such as images. Also the parent StatusStrip control seems to require the LayoutStyle be set to the default of Table.

I am using the following class which inherits from ToolStripStatusLabel and adds the feature to ellipsis text which is too long to display in the control.

[ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.StatusStrip)]
    public partial class SpringLabel : ToolStripStatusLabel
    {
        public SpringLabel()
        {
            this.Spring = true;
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            var flags = TextFormatFlags.Left | TextFormatFlags.EndEllipsis | TextFormatFlags.VerticalCenter;
            var bounds = new Rectangle(0, 0, this.Bounds.Width, this.Bounds.Height);
            TextRenderer.DrawText(e.Graphics, this.Text, this.Font, bounds, this.ForeColor, flags);
        }
    }

This works fine but is missing support for various properties of the parent control, such as BorderSides as well as any support for images and so on. Is there a way to get at the ToolStripStatusLabel code to try and copy or emulate some of its behavior? I really have no ideas on where to start looking.

Edited to add the solution as suggested by @Hans

[ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.StatusStrip)]
    public partial class SpringLabel : ToolStripStatusLabel
    {
        private string? _sltext;
        public new string? Text
        {
            get => _sltext;
            set { _sltext = value; ToolTipText = _sltext; Invalidate(); }
        }
        public SpringLabel()
        {
            this.Spring = true;
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            var flags = TextFormatFlags.Left | TextFormatFlags.EndEllipsis | TextFormatFlags.VerticalCenter;
            var bounds = new Rectangle(0, 0, this.Bounds.Width, this.Bounds.Height);
            TextRenderer.DrawText(e.Graphics, _sltext, this.Font, bounds, this.ForeColor, flags);
        }
    }

This works just fine but likely doesn't support some of the properties such as images. Also the parent StatusStrip control seems to require the LayoutStyle be set to the default of Table.

Share Improve this question edited Feb 2 at 4:13 Steve Stover asked Feb 1 at 20:03 Steve StoverSteve Stover 672 silver badges11 bronze badges 5
  • 2 Call base.OnPaint() to get the goodies. That also paints the text, so you probably want to keep the Text property at "" and add a property to represent the text. Call this.Invalidate() in that property's setter to get a paint. – Hans Passant Commented Feb 1 at 20:09
  • Well a ToolStripStatusLabel has a finite length based on its location on the parent Form and its size, and as such may be too small to show all text in the control. Hence the creation of this SpringLabel with autoellipsis. Of course I am also setting the ToolTipText property to the same text and remembering to set the parent StatusStrip ShowItemToolTips true as well. (This reply was directed to another comment which seems to have been removed, but is nonetheless relevant). – Steve Stover Commented Feb 1 at 21:11
  • @Hans, thanks I have edited my post to reflect your suggestion. – Steve Stover Commented Feb 2 at 4:15
  • If anyone is trying this and has issues with "CS8632" warning, then add the line #nullable enable at the top of the class just before the private string? _sltext line. – Steve Stover Commented Feb 2 at 4:54
  • Using override on the Text field doesn't work properly when calling base.OnPaint. Then the control trys to draw the text twice. However using public new string Text and thus hiding the base field works fine. – Steve Stover Commented Feb 2 at 13:50
Add a comment  | 

1 Answer 1

Reset to default 2

You'll not get the desired result by creating a custom ToolStripStatusLabel unless you draw all parts of the label yourself. The image, text, borders ...etc. Each has a set of properties that the default renderer uses to draw them in its overridable OnRenderXXX methods. Which means, you can instead create a custom renderer, override the relevant methods to draw the different parts of the label the way you want or to adjust the properties of the specialized xxxEventArgs - if possible - and call the base method to do the rest.

Take the following for example assuming the long text is set at runtime.

public class TSRenderer : ToolStripProfessionalRenderer
{
    protected override void OnRenderItemText(ToolStripItemTextRenderEventArgs e)
    {            
        if (e.Item is ToolStripStatusLabel lbl)
        {
            if (lbl.Image is not null)
            {
                Rectangle r = e.TextRectangle;
                Size scaleSize = lbl.Owner!.ImageScalingSize;

                if (r.X < scaleSize.Width)
                {
                    r.X += scaleSize.Width + 1;
                    r.Width -= r.X + 1;
                }

                e.TextRectangle = r;
            }

            e.TextFormat = TextFormatFlags.Left |
                TextFormatFlags.VerticalCenter |
                TextFormatFlags.EndEllipsis;                    
        }            

        base.OnRenderItemText(e);
    }

    protected override void OnRenderItemImage(ToolStripItemImageRenderEventArgs e)
    {
        if (e.Item is ToolStripStatusLabel && e.Image is not null)
        {
            var r = e.ImageRectangle;
            r.Width = r.Height;
            r.X += 2;
            e.Graphics.DrawImage(e.Image, r);
        }
        else
        {
            base.OnRenderItemImage(e);
        }
    }
}

// Use it...

public partial class SomeForm : Form
{
    public SomeForm()
    {
        InitializeComponent();
        yourStatusStrip.Renderer = new TSRenderer();
        someToolStripStatusLabel.AutoSize = false; // Must do.
        someToolStripStatusLabel.Spring = true;
    }
}

... and that's all.

Note, when the text length exceeds the width of the available space, the base renderer does not render the image. It takes the width from the image space and appends it to the text space. Therefore, you need to override this by design behavior in the OnRenderItemImage method override and draw the image yourself regardless of the length of the text. This also requires overriding the OnRenderItemText method to - in addition to apply the required TextFormatFlags.EndEllipsis flag - fix the text rectangle so that it does not overlap the image rectangle since the renderer draws the image before drawing the text.

One final note, set the ToolStripStatusLabel.AutoSize property to false to ensure that overridden methods are also called when the container Form is minimized and restored. And if you need to set the long text in the designer, then make sure to set the ToolStripStatutsLabel.AutoSize property to false, the .Text property to the long text, and the .Spring property to true respectively.

At this point, the solution appears only at runtime. The designer does not reflect the work of the custom renderer unless you create a custom StatusStrip.

public class StatusStripEx : StatusStrip
{
    protected override void OnHandleCreated(EventArgs e)
    {
        base.OnHandleCreated(e);
        Renderer = new TSRenderer();
    }
}
转载请注明原文地址:http://anycun.com/QandA/1744817412a88028.html