如何自定义ToolStripTextBox的渲染?

时间:2009-11-19 10:02:48

标签: c# .net textbox toolstrip renderer

我非常喜欢ToolStripProfessionalRenderer样式,但我不喜欢它呈现ToolStripTextBox的方式。在这里,ToolStripSystemRenderer可以更好地完成IMO。现在有没有办法将渲染器的行为结合起来使用文本框的系统样式和其他一切的专业风格?我成功地设法使用专业风格的按钮和系统风格(通过派生两个类)。但是,ToolStrip中的文本框似乎不会由渲染器处理。使用.NET Reflector,这些文本框似乎甚至没有Paint事件处理程序,尽管它由ToolStrip.OnPaint方法调用。我想知道在哪里编写这样一个文本框的代码,以及它如何配置为像所有其他文本框一样绘制文本框。

2 个答案:

答案 0 :(得分:4)

如果您只想要系统渲染,最简单的方法是使用ToolStripControlHost:

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

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

我已经采取了简单的方法,将底层TextBox直接暴露给表单设计器,而不是委托其所有属性。显然,如果需要,您可以编写所有属性delgation代码。

另一方面,如果有人想做真正的自定义渲染,我会告诉你ToolStripTextBox的作用。它不是直接托管TextBox,而是托管一个名为ToolStripTextBoxControl的私有派生类。此类重写其WndProc以直接处理WM_NCPAINT。然后,它不是将实际绘图委托给渲染器,而是检查渲染器的类型,然后分支到ToolStripTextBoxControl内的不同渲染代码。这很难看。

答案 1 :(得分:1)

也许没有必要潜入“WndProc”。这是在没有它的情况下完成的:

This was done without "WndProc"

问题实际上是如何制作一个“外观漂亮”的TextBox,因为如j__m所述,您可以使用ToolStripControlHost来托管工具条中的自定义控件。

更多信息: http://msdn.microsoft.com/en-us/library/system.windows.forms.toolstripcontrolhost.aspx

如文档所述,您使用的控件可以是自定义控件。

首先,制作自定义TextBox控件非常棘手。如果你想去:

public partial class TextBoxOwnerDraw : TextBox

你陷入了巨大的麻烦!但它不一定是。这是一个小技巧:

如果您将自定义控件设置为Panel,则将TextBox添加到Panel,然后将Textbox边框设置为None ...您可以实现上述结果,最重要的是,它只是一个常规的旧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

}

现在为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;
    }
}

然后当您想要使用它时,只需将其添加到工具栏:

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

或者你想添加它。如果您在Visual Studio中,预览窗口支持呈现此控件。

只有一件事要记住,当访问带有实际文本的TextBox时,它是:

tBox.ToolStripTextBox.Text;