自定义文本框不绘制

Custom TextBox Not Drawing

我根据 this other SO post 自定义了 TextBox class,以便在我的应用程序中包含带有自定义边框的文本框。如果我在表单设计器中设置任何新的自定义属性,它们会暂时出现,直到我更改控件焦点,并且当我 运行 应用程序时,新的边框设置不会显示。我确实更新了表单的 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
}

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

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

是的,您可以对它们进行编码,是的,它们会有一些效果。但是 TextBox 不按照正常规则进行游戏,并且会在不触发绘画事件的情况下弄乱您的绘图。

也许您可以将自己连接到 windows 消息队列 (WndProc),但通常不推荐这样做,尤其是在使用边框装饰之类的情况下。

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

您需要覆盖 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 );
}