Как настроить рендеринг ToolStripTextBox?

Мне очень нравится стиль ToolStripProfessionalRenderer, но мне не нравится, как он отображает ToolStripTextBox. Здесь ToolStripSystemRenderer работает лучше, IMO. Теперь есть способ объединить поведение обоих рендереров, чтобы использовать системный стиль для текстовых полей и профессиональный стиль для всего остального? Мне удалось использовать профессиональный стиль для кнопок и системный стиль для остальных (путем получения обоих классов). Но текстовые поля в ToolStrip, похоже, не обрабатываются визуализатором. При использовании .NET Reflector кажется, что эти текстовые поля даже не имеют обработчика событий Paint, хотя он вызывается методом ToolStrip.OnPaint. Мне интересно, где вообще находится код для рисования такого текстового поля и как его можно настроить для рисования текстового поля, как и все другие текстовые поля.


person ygoe    schedule 19.11.2009    source источник
comment
Windows Forms -- [мин. 15 символов для stackoverflow]   -  person ygoe    schedule 23.12.2010


Ответы (2)


Если вам просто нужен системный рендеринг, проще всего вместо этого использовать ToolStripControlHost:

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

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

Здесь я пошел по простому пути и представил лежащий в основе TextBox непосредственно конструктору форм, вместо того, чтобы делегировать все его свойства. Очевидно, вы можете написать весь код делегирования свойств, если хотите.

С другой стороны, если кто-то хочет сделать действительно индивидуальный рендеринг, я расскажу вам, что делает ToolStripTextBox. Вместо непосредственного размещения TextBox в нем размещается частный производный класс с именем ToolStripTextBoxControl. Этот класс переопределяет свой WndProc, чтобы напрямую обрабатывать WM_NCPAINT. А затем вместо делегирования фактического рисования средству визуализации он проверяет тип средства визуализации, а затем переходит к другому коду визуализации внутри элемента управления ToolStripTextBoxControl. Это довольно некрасиво.

person j__m    schedule 14.01.2011

Возможно, вам также не потребуется погружаться в «WndProc». Это было сделано без него:

Это было сделано без WndProc

На самом деле вопрос заключается в том, как сделать «красивый» TextBox, потому что, как описано j__m, вы можете просто использовать ToolStripControlHost для размещения пользовательского элемента управления в своей полосе инструментов.

Подробнее здесь: http://msdn.microsoft.com/en-us/library/system.windows.forms.toolstripcontrolhost.aspx

Как указано в документации, используемый вами элемент управления может быть пользовательским элементом управления.

Во-первых, безумно сложно создать собственный элемент управления TextBox. Если ты хочешь идти:

public partial class TextBoxOwnerDraw : TextBox

Вас ждут ОГРОМНЫЕ неприятности! Но это не обязательно. Вот небольшая хитрость:

Если вы создадите настраиваемый элемент управления в виде панели, затем добавите TextBox в панель, затем установите для границ 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;
person Conrad de Wet    schedule 24.07.2013