自定义文本框未绘制

时间:2018-10-02 17:28:37

标签: c# winforms textbox visual-studio-2017 custom-controls

我已经创建了一个自定义TextBox类,以基于this other SO post在我的应用程序中有一个带有自定义边框的文本框。如果我在表单设计器中设置了任何新的自定义属性,它们会暂时显示,直到更改控件焦点为止,并且在运行应用程序时,不会显示新的边框设置。我确实更新了表单的InitializeComponent方法,以便文本框初始化一个新的BorderedTextBox而不是TextBox。有人知道这是怎么回事吗?

public class BorderedTextBox : TextBox
{
    private Color _borderColor = Color.Black;
    private int _borderWidth = 2;
    private int _borderRadius = 5;

    public BorderedTextBox() : base()
    {
        InitializeComponent();
        this.Paint += this.BorderedTextBox_Paint;
    }

    public BorderedTextBox(int width, int radius, Color color) : base()
    {
        this._borderWidth = Math.Max(1, width);
        this._borderColor = color;
        this._borderRadius = Math.Max(0, radius);
        InitializeComponent();
        this.Paint += this.BorderedTextBox_Paint;
    }

    public Color BorderColor
    {
        get => this._borderColor;
        set
        {
            this._borderColor = value;
            DrawTextBox();
        }
    }

    public int BorderWidth
    {
        get => this._borderWidth;
        set
        {
            if (value > 0)
            {
                this._borderWidth = Math.Min(value, 10);
                DrawTextBox();
            }
        }
    }

    public int BorderRadius
    {
        get => this._borderRadius;
        set
        {   // Setting a radius of 0 produces square corners...
            if (value >= 0)
            {
                this._borderRadius = value;
                this.DrawTextBox();
            }
        }
    }

    private void BorderedTextBox_Paint(object sender, PaintEventArgs e) => DrawTextBox(e.Graphics);

    private void DrawTextBox() => this.DrawTextBox(this.CreateGraphics());

    private void DrawTextBox(Graphics g)
    {
        Brush borderBrush = new SolidBrush(this.BorderColor);
        Pen borderPen = new Pen(borderBrush, (float)this._borderWidth);
        Rectangle rect = new Rectangle(
            this.ClientRectangle.X,
            this.ClientRectangle.Y,
            this.ClientRectangle.Width - 1,
            this.ClientRectangle.Height - 1);

        // Clear text and border
        g.Clear(this.BackColor);

        // Drawing Border
        g.DrawRoundedRectangle(
            borderPen,
            (0 == this._borderWidth % 2) ? rect.X + this._borderWidth / 2 : rect.X + 1 + this._borderWidth / 2,
            rect.Y,
            rect.Width - this._borderWidth,
            (0 == this._borderWidth % 2) ? rect.Height - this._borderWidth / 2 : rect.Height - 1 - this._borderWidth / 2,
            (float)this._borderRadius);
    }

    #region Component Designer generated code
    /// <summary>Required designer variable.</summary>
    private System.ComponentModel.IContainer components = null;

    /// <summary>Clean up any resources being used.</summary>
    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
            components.Dispose();

        base.Dispose(disposing);
    }

    /// <summary>Required method for Designer support - Don't modify!</summary>
    private void InitializeComponent() => components = new System.ComponentModel.Container();
    #endregion
}

2 个答案:

答案 0 :(得分:3)

您需要覆盖WndProc:

private const int WM_PAINT = 0x000F;

protected override void WndProc( ref Message m ) {

    if(m.Msg == WM_PAINT ) {

            base.WndProc( ref m );

            Graphics gr = this.CreateGraphics();

            //draw what you want


            gr.Dispose();

            return;
        }

        base.WndProc( ref m );
    }

工作正常,没有任何问题。它虽然吸引客户区。绘制自定义边框的完整版本,textbox必须具有边框:

[DllImport( "user32.dll" )]
static extern IntPtr GetWindowDC( IntPtr hWnd );

[DllImport( "user32.dll" )]
static extern bool ReleaseDC( IntPtr hWnd, IntPtr hDC );

[DllImport( "gdi32.dll" )]
static extern bool FillRgn( IntPtr hdc, IntPtr hrgn, IntPtr hbr );

[DllImport( "gdi32.dll" )]
static extern IntPtr CreateRectRgn( int nLeftRect, int nTopRect, int nRightRect,
        int nBottomRect );

[DllImport( "gdi32.dll" )]
static extern IntPtr CreateSolidBrush( uint crColor );

[DllImport( "gdi32.dll" )]
static extern bool DeleteObject( IntPtr hObject );

private const int WM_NCPAINT = 0x0085;
private const int WM_PAINT = 0x000F;
private const int RGN_DIFF = 0x4;
private int p_border = 2;

protected override void WndProc( ref Message m ) {

    if(m.Msg == WM_PAINT ) {
        base.WndProc( ref m );

        IntPtr hdc = GetWindowDC( this.Handle ); //gr.GetHdc();
        IntPtr rgn = CreateRectRgn( 0, 0, this.Width, this.Height );
        IntPtr brush = CreateSolidBrush( 0xFF0000 ); //Blue : B G R

        CombineRgn( rgn, rgn, CreateRectRgn( p_border, p_border, this.Width - p_border,
                                             this.Height - p_border ), RGN_DIFF );

        FillRgn( hdc, rgn, brush );

        ReleaseDC( this.Handle, hdc );
        DeleteObject( rgn );
        DeleteObject( brush );

        m.Result = IntPtr.Zero;

        return;
    }

    if( m.Msg == WM_NCPAINT ) {
        return;
    }

    base.WndProc( ref m );
}

答案 1 :(得分:1)

Winforms TextBox是一个旧控件,我认为甚至可以追溯到.Net框架之前。

它不支持所有者绘图,正如在MSDN上看到的那样,PaintOnPaint都没有文件记录。

是的,您可以对它们进行编码,是的,它们会产生一些效果。但是TextBox不能遵循正常规则,并且会在不触发绘画事件的情况下使您的绘图混乱。

可能您可以将自己挂在Windows消息队列(WndProc)中,但是通常不建议这样做,尤其是对于带有边框的装饰。

通常,最简单的解决方案是将TextBox嵌套在Panel中,然后让Panel绘制出漂亮的Border