Search code examples
c#controlsrenderdrawtoolstrip

C# ToolStripProfessionalRender, event OnRenderItemText applied only on the first item


I'm drawing a custom toolstrip using ToolStripProfessionalRender and editing the OnRenderItemText event as follows:

protected override void OnRenderItemText(ToolStripItemTextRenderEventArgs e)
{
    e.Item.ForeColor = Clr.White;
    e.Item.TextAlign = ContentAlignment.MiddleLeft;
    e.Item.Alignment = ToolStripItemAlignment.Left;
    base.OnRenderItemText(e);
    if (e.Item.GetType() == typeof(ToolStripDropDownButton))
    {
        ToolStripDropDownButton tsmi = (ToolStripDropDownButton)e.Item;
        if (tsmi.HasDropDownItems && tsmi.OwnerItem == null)
        {
            Rectangle bounds = tsmi.Bounds;
            bounds.X = bounds.Right - 25;
            bounds.Width = 25;
            bounds.Y = 10;

            // Draw the corner
            Graphics G = e.Graphics;
            SolidBrush brushw = new SolidBrush(Color.FromArgb(70,70,70));
            Point[] points =
            {
                new Point(bounds.Right - 3, bounds.Height - 11), // point top right
                new Point(bounds.Right - 3, bounds.Bottom - 14), // point bottom right
                new Point(bounds.Right - 10, bounds.Bottom - 14) // point bottom left

            };
            G.FillPolygon(brushw, points);
        }
    }
}

and basically the output i'm trying to obtain is the following: enter image description here

So drawing a little triangle on the bottom right corner when i got a ToolStripDropDownButton. The problem is that the little triangle is drawn only first item.

To end up the question i draw this toolstrip dynamically using a function that adds a dropdownbutton at each call.

ToolStripDropDownButton m_Item = new ToolStripDropDownButton(text, image);
                        m_Item.ImageAlign = ContentAlignment.MiddleCenter;
                        m_Item.ImageScaling = ToolStripItemImageScaling.None;
                        m_Item.Name = name;
                        m_Item.ForeColor = Color.White;
                        m_Item.BackColor = Color.FromArgb(95, 95, 95);
                        m_Item.Padding = new Padding(5);
                        m_Item.ShowDropDownArrow = false;
                        m_Item.Paint += new PaintEventHandler(this.PaintButtonBorder);
                        if (tabPage != null)
                            m_Item.Click += (sender, e) => AddClickTab(sender, e, tabPage);
                        ((ToolStripDropDownMenu)m_Item.DropDown).ShowImageMargin = false;
                        ((ToolStripDropDownMenu)m_Item.DropDown).ShowCheckMargin = false;
                        ((ToolStripDropDownMenu)m_Item.DropDown).Cursor = Cursors.Hand;
                        toolStrip1.Items.Add(m_Item);

                        if (SubItems != null)
                        {
                            for(int i = 0; i < SubItems.Length; i++)
                            {
                                object[] subitem = (object[])SubItems[i];
                                FnAddToolStripMenuItem(
                                    subitem[0].ToString(),
                                    subitem[1].ToString(),
                                    (Bitmap)subitem[2],
                                    m_Item,
                                    (TabPage)subitem[3]
                                    );
                            }
                        }

Am i missing some "new" maybe?


Solution

  • Override the OnRenderItemText method only to draw the text part as it says, and/or to set the default properties used when rendering the text. To change the look and the shape of the arrows of the dropdown items, override the OnRenderArrow method.

    Example

    using System.Drawing;
    using System.Drawing.Drawing2D;
    
    protected override void OnRenderArrow(ToolStripArrowRenderEventArgs e)
    {
        // Optional: to be the default color of the arrows.
        e.ArrowColor = Color.FromArgb(70, 70, 70);
    
        if (e.Item is ToolStripDropDownButton item && item.OwnerItem == null)
        {
            var g = e.Graphics;
            var r = new Rectangle(item.Bounds.Width - 10, item.Bounds.Height - 10, 8, 8);
    
            g.SmoothingMode = SmoothingMode.AntiAlias;
            g.PixelOffsetMode = PixelOffsetMode.Half;
    
            using (var br = new SolidBrush(e.ArrowColor))
                g.FillPolygon(br, new[]
                {
                    new Point(r.Left, r.Bottom),
                    new Point(r.Right, r.Top),
                    new Point(r.Right, r.Bottom)
                });
    
            g.SmoothingMode = SmoothingMode.None;
            g.PixelOffsetMode = PixelOffsetMode.Default;
        }
        else
            base.OnRenderArrow(e);
    }
    
    protected override void OnRenderItemText(ToolStripItemTextRenderEventArgs e)
    {
        e.Item.ForeColor = Color.White;
        e.Item.TextAlign = ContentAlignment.MiddleLeft;
        e.Item.Alignment = ToolStripItemAlignment.Left;
        base.OnRenderItemText(e);
    }
    

    Make sure to enable the ShowDropDownArrow property of the dropdown buttons. So comment this m_Item.ShowDropDownArrow = false;.

    If you are also interested to change the color according to the current state of the dropdown button (Selected, Pressed), then you can do for example:

    using (var br = new SolidBrush(item.Selected 
        ? Color.FromArgb(150, 150, 150) : item.Pressed 
        ? Color.FromArgb(100, 100, 100) :
          Color.FromArgb(70, 70, 70)))
        //...