在C#中更改文本框边框颜色

9
我有以下代码:

我有以下代码:

public class OurTextBox : TextBox
{
    public OurTextBox()
        : base()
    {
        this.SetStyle(ControlStyles.UserPaint, true);
    }

    protected override void OnPaint(PaintEventArgs e)
    {
         base.OnPaint(e);
         Pen penBorder = new Pen(Color.Gray, 1);
         Rectangle rectBorder = new Rectangle(e.ClipRectangle.X, e.ClipRectangle.Y, e.ClipRectangle.Width - 1, e.ClipRectangle.Height - 1);
         e.Graphics.DrawRectangle(penBorder, rectBorder);
   }
}

这个功能运行良好,但是在获得焦点之前不会显示文本。有人能帮我吗?出了什么问题?

你不应该使用 OnPaint 来为 TextBox 绘制边框。相反,你应该在 WndProc 中处理 WM_NCPAINT 消息,并在非客户区绘制边框,就像这样 - Reza Aghaei
4个回答

36

要更改TextBox的边框颜色,您可以重写WndProc方法并处理WM_NCPAINT消息。然后使用GetWindowDC获取控件的窗口设备上下文,因为我们要绘制控件的非客户区域。然后只需从该上下文创建一个Graphics对象,然后为控件绘制边框即可。

要在BorderColor属性更改时重新绘制控件,可以使用RedrawWindow方法。

代码

这是一个具有BorderColor属性的TextBox。如果属性值与Color.Transparent不同且BorderStyle是其默认值Fixed3d,则控件将使用BorderColor

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class MyTextBox : TextBox {
    const int WM_NCPAINT = 0x85;
    const uint RDW_INVALIDATE = 0x1;
    const uint RDW_IUPDATENOW = 0x100;
    const uint RDW_FRAME = 0x400;
    [DllImport("user32.dll")]
    static extern IntPtr GetWindowDC(IntPtr hWnd);
    [DllImport("user32.dll")]
    static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
    [DllImport("user32.dll")]
    static extern bool RedrawWindow(IntPtr hWnd, IntPtr lprc, IntPtr hrgn, uint flags);
    Color borderColor = Color.Blue;
    public Color BorderColor {
        get { return borderColor; }
        set { borderColor = value;
            RedrawWindow(Handle, IntPtr.Zero, IntPtr.Zero,
                RDW_FRAME | RDW_IUPDATENOW | RDW_INVALIDATE);
        }
    }
    protected override void WndProc(ref Message m) {
        base.WndProc(ref m);
        if (m.Msg == WM_NCPAINT && BorderColor != Color.Transparent &&
            BorderStyle == System.Windows.Forms.BorderStyle.Fixed3D) {
            var hdc = GetWindowDC(this.Handle);
            using (var g = Graphics.FromHdcInternal(hdc))
            using (var p = new Pen(BorderColor))
                g.DrawRectangle(p, new Rectangle(0, 0, Width - 1, Height - 1));
            ReleaseDC(this.Handle, hdc);
        }
    }
    protected override void OnSizeChanged(EventArgs e) {
        base.OnSizeChanged(e);
        RedrawWindow(Handle, IntPtr.Zero, IntPtr.Zero,
               RDW_FRAME | RDW_IUPDATENOW | RDW_INVALIDATE);
    }
}

结果

这里是使用不同颜色和状态的结果。所有边框样式均被支持,如下图所示,并且您可以使用任何颜色来设置边框:

enter image description here

下载

您可以克隆或下载工作示例:


1
@Misiu,我确认了这个问题,我会尝试解决它。 - Reza Aghaei
2
@user3700562,我目前没有Win7的访问权限。一旦我有时间设置Win7虚拟机,我会看看那个问题。但在Win 8.1和10上它是正常工作的。 - Reza Aghaei
1
@stuzor Fixed3D是显示颜色的样式。FixedSingle将使用默认的FixedSingle样式进行绘制。 - Reza Aghaei
1
@stuzor 上述类中的 Fixed3D 将充当 FixedSingle。它绘制一个实心的单线颜色边框。 - Reza Aghaei
2
@stuzor 你会得到一条细白线的原因是带有Fixed3D边框的文本框的非客户区是一个两像素厚的矩形。为了处理背景颜色问题,只需将上述代码的核心更改为:var hdc = GetWindowDC(this.Handle); using (var g = Graphics.FromHdcInternal(hdc)) { using (var p = new Pen(BorderColor)) g.DrawRectangle(p, new Rectangle(0, 0, Width - 1, Height - 1)); using (var b = new Pen(BackColor)) g.DrawRectangle(b, new Rectangle(1, 1, Width - 3, Height - 3)); } ReleaseDC(this.Handle, hdc); - David Fletcher
显示剩余13条评论

6

您还需要手动绘制文本。

protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
    Pen penBorder = new Pen(Color.Gray, 1);
    Rectangle rectBorder = new Rectangle(e.ClipRectangle.X, e.ClipRectangle.Y, e.ClipRectangle.Width - 1, e.ClipRectangle.Height - 1);
    e.Graphics.DrawRectangle(penBorder, rectBorder);

    Rectangle textRec = new Rectangle(e.ClipRectangle.X + 1, e.ClipRectangle.Y + 1, e.ClipRectangle.Width - 1, e.ClipRectangle.Height - 1);
    TextRenderer.DrawText(e.Graphics, Text, this.Font, textRec, this.ForeColor, this.BackColor, TextFormatFlags.Default);
}

如果 TextRenderer 无法满足您的需求,您可以尝试使用 e.Graphics.DrawString() 方法(我总是倾向于使用这种方法,因为它能够得到更好的结果)。


4
不应覆盖“OnPaint”,因为需要绘制字符串、闪烁的光标、选择高亮等。相反,应在“WndProc”中处理“WM_NCPAINT”消息并在非客户区绘制边框,如此处所示。 - Reza Aghaei

1

有几种方法可以实现这个目标,但没有一种是理想的。这只是WinForms的本质。但是,你有一些选择。我将总结:

你可以通过将TextBox嵌入到Panel中来实现你想要的效果。

public class BorderedTextBox : Panel 
{
    private TextBox textBox;
    private bool focusedAlways = false;
    private Color normalBorderColor = Color.Gray;
    private Color focusedBorderColor = Color.Red;

    public BorderTextBox() 
    {
        this.DoubleBuffered = true;
        this.Padding = new Padding(2);

        this.TextBox = new TextBox();
        this.TextBox.AutoSize = false;
        this.TextBox.BorderStyle = BorderStyle.None;
        this.TextBox.Dock = DockStyle.Fill;
        this.TextBox.Enter += new EventHandler(this.TextBox_Refresh);
        this.TextBox.Leave += new EventHandler(this.TextBox_Refresh);
        this.TextBox.Resize += new EventHandler(this.TextBox_Refresh);
        this.Controls.Add(this.TextBox);
    }

    private void TextBox_Refresh(object sender, EventArgs e) 
    {
        this.Invalidate();
    }

    protected override void OnPaint(PaintEventArgs e) 
    {
        e.Graphics.Clear(SystemColors.Window);
        using (Pen borderPen = new Pen(this.TextBox.Focused || FocusedAlways ? 
            focusedBorderColor : normalBorderColor)) 
        {
            e.Graphics.DrawRectangle(borderPen, 
                new Rectangle(0, 0, this.ClientSize.Width - 1, this.ClientSize.Height - 1));
        }
        base.OnPaint(e);
    }

    public TextBox TextBox
    {
        get { return textbox; }
        set { textbox = value; }
    }

    public bool FocusedAlaways
    {
        get { return focusedAlways; }
        set { focusedAlways = value; }
    }
}

你也可以不重写任何控件来实现这一点,但上述方法更好。上述方法将在控件获得焦点时绘制边框。如果你想要永久显示边框,请将 FocusedAlways 属性设置为 True
我希望这能帮到你。

1
哇,为了一个简单的边框需要做如此大量的工作。这就是为什么我想我们应该总是使用 WPF :) - Nikita B
1
我刚刚开始接触WPF,虽然很喜欢它,但是从WinForms背景转过来还是有些困难。真希望一开始就学习WPF。MVVM模式确实有些难以理解,但整个框架肯定更好!现在我有一个WinForm项目,其中包含了我多年来积累的大量解决方案。 - MoonKnight
1
谢谢。我同意Nick的观点...改变一个边框需要这么多的工作,真是太不可思议了。 - Moisés Martínez
我认为,正如Nik所指出的那样。这就是WPF诞生的原因。就我个人而言,我非常厌倦每天都要做这种事情! - MoonKnight
为了永久绘制边框,使用派生的 TextBox 控件比包含 TextBoxPanel 更友好、更优雅。此外,如果您只想在焦点上突出显示边框,可以简单地使用这种 解决方案 - Reza Aghaei

1

将文本框的边框样式设置为无, 然后在容器窗体的“paint”事件中编写以下代码

    private void Form1_Paint(object sender, PaintEventArgs e)
        {
System.Drawing.Rectangle rect = new Rectangle(TextBox1.Location.X, TextBox1.Location.Y, TextBox1.ClientSize.Width, TextBox1.ClientSize.Height);

                rect.Inflate(1, 1); // border thickness
                System.Windows.Forms.ControlPaint.DrawBorder(e.Graphics, rect, Color.DeepSkyBlue, ButtonBorderStyle.Solid);

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接