How to customise rendering of a ToolStripTextBox?

吃可爱长大的小学妹 提交于 2019-12-06 03:05:09

If you just want system rendering, the easiest approach is to use ToolStripControlHost instead:

class ToolStripSystemTextBox : ToolStripControlHost
{
   public ToolStripSystemTextBox : base(new TextBox()) { }

   [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
   [TypeConverter(typeof(ExpandableObjectConverter))]
   public TextBox TextBox { get { return Control as TextBox; } }
}

I've taken the easy way out here and exposed the underlying TextBox directly to the form designer, instead of delegating all its properties. Obviously you can write all the property delgation code if you want.

On the other hand, if anyone wants to do truly custom rendering, I'll tell you what ToolStripTextBox does. Instead of hosting a TextBox directly, it hosts a private derived class called ToolStripTextBoxControl. This class overrides its WndProc in order to directly handle WM_NCPAINT. And then instead of delegating the actual drawing to the Renderer, it checks the Renderer's Type, and then branches to different rendering code inside of ToolStripTextBoxControl. It's pretty ugly.

It may not be necessary to dive into "WndProc" either. This was done without it:

The Question really is how do you make a "nice looking" TextBox, because as described by j__m, you can just use ToolStripControlHost, to host a custom control in your tool strip.

More here: http://msdn.microsoft.com/en-us/library/system.windows.forms.toolstripcontrolhost.aspx

And as documented, the control you use can be a Custom Control.

Firstly, It's insanely tricky to make a custom TextBox Control. If you want to go:

public partial class TextBoxOwnerDraw : TextBox

You are in for HUGE trouble! But it doesn't have to be. Here is a little trick:

If you make a custom control as a Panel, then add the TextBox to the Panel, then set the Textbox borders to None... you can achieve the result as above, and best of all, its just a regular old TextBox, so cut copy paste all works, right click works!

Ok, here is the code for a nice looking textbox:

public partial class TextBoxOwnerDraw : Panel
{
    private TextBox MyTextBox;
    private int cornerRadius = 1;
    private Color borderColor = Color.Black;
    private int borderSize = 1;
    private Size preferredSize = new Size(120, 25); // Use 25 for height, so it sits in the middle

    /// <summary>
    /// Access the textbox
    /// </summary>
    public TextBox TextBox
    {
        get { return MyTextBox; }
    }
    public int CornerRadius
    {
        get { return cornerRadius; }
        set
        {
            cornerRadius = value;
            RestyleTextBox();
            this.Invalidate();
        }
    }
    public Color BorderColor
    {
        get { return borderColor; }
        set
        {
            borderColor = value;
            RestyleTextBox();
            this.Invalidate();
        }
    }
    public int BorderSize
    {
        get { return borderSize; }
        set
        {
            borderSize = value;
            RestyleTextBox();
            this.Invalidate();
        }
    }
    public Size PrefSize
    {
        get { return preferredSize; }
        set
        {
            preferredSize = value;
            RestyleTextBox();
            this.Invalidate();
        }
    }

    public TextBoxOwnerDraw()
    {
        MyTextBox = new TextBox();
        this.Controls.Add(MyTextBox);
        RestyleTextBox();
    }

    private void RestyleTextBox()
    {
        double TopPos = Math.Floor(((double)this.preferredSize.Height / 2) - ((double)MyTextBox.Height / 2));

        MyTextBox.BackColor = Color.White;
        MyTextBox.BorderStyle = BorderStyle.None;
        MyTextBox.Multiline = false;
        MyTextBox.Top = (int)TopPos;
        MyTextBox.Left = this.BorderSize;
        MyTextBox.Width = preferredSize.Width - (this.BorderSize * 2);

        this.Height = MyTextBox.Height + (this.BorderSize * 2); // Will be ignored, but if you use elsewhere
        this.Width = preferredSize.Width;
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        if (cornerRadius > 0 && borderSize > 0)
        {
            Graphics g = e.Graphics;
            g.SmoothingMode = SmoothingMode.AntiAlias;

            Rectangle cRect = this.ClientRectangle;
            Rectangle safeRect = new Rectangle(cRect.X, cRect.Y, cRect.Width - this.BorderSize, cRect.Height - this.BorderSize);

            // Background color
            using (Brush bgBrush = new SolidBrush(MyTextBox.BackColor))
            {
                DrawRoundRect(g, bgBrush, safeRect, (float)this.CornerRadius);
            }
            // Border
            using (Pen borderPen = new Pen(this.BorderColor, (float)this.BorderSize))
            {
                DrawRoundRect(g, borderPen, safeRect, (float)this.CornerRadius);
            }
        }
        base.OnPaint(e);
    }

    #region Private Methods
    private GraphicsPath getRoundRect(int x, int y, int width, int height, float radius)
    {
        GraphicsPath gp = new GraphicsPath();
        gp.AddLine(x + radius, y, x + width - (radius * 2), y); // Line
        gp.AddArc(x + width - (radius * 2), y, radius * 2, radius * 2, 270, 90); // Corner (Top Right)
        gp.AddLine(x + width, y + radius, x + width, y + height - (radius * 2)); // Line
        gp.AddArc(x + width - (radius * 2), y + height - (radius * 2), radius * 2, radius * 2, 0, 90); // Corner (Bottom Right)
        gp.AddLine(x + width - (radius * 2), y + height, x + radius, y + height); // Line
        gp.AddArc(x, y + height - (radius * 2), radius * 2, radius * 2, 90, 90); // Corner (Bottom Left)
        gp.AddLine(x, y + height - (radius * 2), x, y + radius); // Line
        gp.AddArc(x, y, radius * 2, radius * 2, 180, 90); // Corner (Top Left)
        gp.CloseFigure();
        return gp;
    }
    private void DrawRoundRect(Graphics g, Pen p, Rectangle rect, float radius)
    {
        GraphicsPath gp = getRoundRect(rect.X, rect.Y, rect.Width, rect.Height, radius);
        g.DrawPath(p, gp);
        gp.Dispose();
    }
    private void DrawRoundRect(Graphics g, Pen p, int x, int y, int width, int height, float radius)
    {
        GraphicsPath gp = getRoundRect(x, y, width, height, radius);
        g.DrawPath(p, gp);
        gp.Dispose();
    }
    private void DrawRoundRect(Graphics g, Brush b, int x, int y, int width, int height, float radius)
    {
        GraphicsPath gp = getRoundRect(x, y, width, height, radius);
        g.FillPath(b, gp);
        gp.Dispose();
    }
    private void DrawRoundRect(Graphics g, Brush b, Rectangle rect, float radius)
    {
        GraphicsPath gp = getRoundRect(rect.X, rect.Y, rect.Width, rect.Height, radius);
        g.FillPath(b, gp);
        gp.Dispose();
    }
    #endregion

}

Now for the ToolStripControlHost

public partial class ToolStripTextBoxOwnerDraw : ToolStripControlHost
{
    private TextBoxOwnerDraw InnerTextBox
    {
        get { return Control as TextBoxOwnerDraw; }
    }

    public ToolStripTextBoxOwnerDraw() : base(new TextBoxOwnerDraw()) { }

    public TextBox ToolStripTextBox
    {
        get { return InnerTextBox.TextBox; }
    }
    public int CornerRadius
    {
        get { return InnerTextBox.CornerRadius; }
        set
        {
            InnerTextBox.CornerRadius = value;
            InnerTextBox.Invalidate();
        }
    }
    public Color BorderColor
    {
        get { return InnerTextBox.BorderColor; }
        set
        {
            InnerTextBox.BorderColor = value;
            InnerTextBox.Invalidate();
        }
    }
    public int BorderSize
    {
        get { return InnerTextBox.BorderSize; }
        set
        {
            InnerTextBox.BorderSize = value;
            InnerTextBox.Invalidate();
        }
    }

    public override Size GetPreferredSize(Size constrainingSize)
    {
        return InnerTextBox.PrefSize;
    }
}

Then When you want to use it, just add it to the tool bar:

ToolStripTextBoxOwnerDraw tBox = new ToolStripTextBoxOwnerDraw();
this.toolStripMain.Items.Add(tBox);

or however you want to add it. If you are in Visual Studio, the preview window supports rendering this Control.

There is only one thing to remember, when accessing the TextBox with the actual text in it, its:

tBox.ToolStripTextBox.Text;
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!