改变文本框的边框颜色。

31

当用户单击或聚焦文本框时,我该如何更改其BorderColor?


如果你正在寻找一个具有BorderColor属性的TextBox,请查看更改TextBox边框颜色 - Reza Aghaei
8个回答

48
你可以处理TextBoxWM_NCPAINT消息,在控件的非客户区绘制边框,如果控件获得了焦点。你可以使用任何颜色来绘制边框:
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class ExTextBox : TextBox
{
    [DllImport("user32")]
    private static extern IntPtr GetWindowDC(IntPtr hwnd);
    private const int WM_NCPAINT = 0x85;
    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);
        if (m.Msg == WM_NCPAINT && this.Focused)
        {
            var dc = GetWindowDC(Handle);
            using (Graphics g = Graphics.FromHdc(dc))
            {
                g.DrawRectangle(Pens.Red, 0, 0, Width - 1, Height - 1);
            }
        }
    }
}

结果

控件聚焦时绘制边框完全不闪烁:

Change TextBox border color on focus

文本框的边框颜色属性

在本篇文章中,我只是在聚焦时更改了边框颜色。您还可以为控件添加一个BorderColor属性。然后,您可以根据设计时或运行时的要求更改边框颜色。我在下一篇文章中发布了一个更完整版本的TextBox,其中包含BorderColor属性:

Change Textbox border color


当按钮的平面样式是标准时,如何做到相同的效果呢?我的意思是当点击按钮时,按钮会出现蓝色的高亮。 - Simple
我尝试了你的解决方案,它很好用,但是也会闪烁。也许我需要使用BufferedGraphics进行双缓冲? - 8749236
@8749236 你也试过我的另一个答案了吗?https://dev59.com/JnTYa4cB1Zd3GeqPqRTl#39420512 如果你试了还是闪烁,告诉我你的操作系统和 .NET 版本,我会尝试修复它。 - Reza Aghaei
@8749236 请查看我的答案 https://dev59.com/i2kw5IYBdhLWcg3wm70h#74106039,它基于Reza Aghaei的代码提供了一个更好的无闪烁自定义TextBox控件。 - Ahmed Osama
@AhmedOsama 谢谢你的提议,我目前没有时间来处理这个。此外,演示gif显示你的窗口存在输入延迟,这不符合我的需求,但它看起来很有趣,当我回到这个问题时可能会有所帮助。 - 8749236
@8749236 这个 GIF 是低质量的,仅用于演示目的,因此不明智从中评判控件的性能表现。 - Ahmed Osama

21

试试这个

bool focus = false;
private void Form1_Paint(object sender, PaintEventArgs e)
{
    if (focus)
    {
        textBox1.BorderStyle = BorderStyle.None;
        Pen p = new Pen(Color.Red);
        Graphics g = e.Graphics;
        int variance = 3;
        g.DrawRectangle(p, new Rectangle(textBox1.Location.X - variance, textBox1.Location.Y - variance, textBox1.Width + variance, textBox1.Height +variance ));
    }
    else
    {
        textBox1.BorderStyle = BorderStyle.FixedSingle;
    }
}

private void textBox1_Enter(object sender, EventArgs e)
{
    focus = true;
    this.Refresh();
}

private void textBox1_Leave(object sender, EventArgs e)
{
    focus = false;
    this.Refresh();
}

谢谢,伙计。它确实起作用了,但它也改变了表单中所有其他文本框的边框颜色。你能解释一下发生了什么以及如何将颜色从蓝色改为红色吗? - Raggy Shrestha
我认为上面的代码不会改变所有文本框的边框。我们所做的是,在textBox1周围绘制一个矩形。 - PraveenVenu
如何在边框上或矩形周围应用不同的颜色。 - Raggy Shrestha
我对@PraVn的代码进行了一些更改,现在我认为它很好看: int方差=1; g.DrawRectangle(p,new Rectangle(textBox1.Location.X-方差,textBox1.Location.Y-方差,textBox1.Width+方差,textBox1.Height+方差)); 方差=2; g.DrawRectangle(p,new Rectangle(textBox1.Location.X-方差,textBox1.Location.Y-方差,textBox1.Width+方差+1,textBox1.Height+方差+1)); - Arash
6
当父控件中有多个控件时,在OnPaint方法中执行任何操作都会影响性能,这是一个不好的想法。要理解我的意思,请尝试在窗体上放置10个控件,并在Form1_Paint中设置断点。现在,对于在Form1上绘制的每个控件,都会绘制在textBox1周围的边框。更好的做法是创建一个自定义控件,从TextBox继承,以确保只在需要时绘制一次边框,而不是不必要地多次绘制,这会大大增加渲染时间,特别是在具有更多自定义onpaint作业时。 - Mike de Klerk

11

以下是一种设置文本框边框颜色的终极解决方案:

public class BorderedTextBox : UserControl
{
    TextBox textBox;

    public BorderedTextBox()
    {
        textBox = new TextBox()
        {
            BorderStyle = BorderStyle.FixedSingle,
            Location = new Point(-1, -1),
            Anchor = AnchorStyles.Top | AnchorStyles.Bottom |
                     AnchorStyles.Left | AnchorStyles.Right
        };
        Control container = new ContainerControl()
        {
            Dock = DockStyle.Fill,
            Padding = new Padding(-1)
        };
        container.Controls.Add(textBox);
        this.Controls.Add(container);

        DefaultBorderColor = SystemColors.ControlDark;
        FocusedBorderColor = Color.Red;
        BackColor = DefaultBorderColor;
        Padding = new Padding(1);
        Size = textBox.Size;
    }

    public Color DefaultBorderColor { get; set; }
    public Color FocusedBorderColor { get; set; }

    public override string Text
    {
        get { return textBox.Text; }
        set { textBox.Text = value; }
    }

    protected override void OnEnter(EventArgs e)
    {
        BackColor = FocusedBorderColor;
        base.OnEnter(e);
    }

    protected override void OnLeave(EventArgs e)
    {
        BackColor = DefaultBorderColor;
        base.OnLeave(e);
    }

    protected override void SetBoundsCore(int x, int y,
        int width, int height, BoundsSpecified specified)
    {
        base.SetBoundsCore(x, y, width, textBox.PreferredHeight, specified);
    }
}

我们可以将这个文本框设置为多行吗? - Tolga Evcimen

5

WinForms在这方面一直表现不佳,有点麻烦。

你可以尝试的一种方法是在Panel中嵌入一个TextBox,然后根据焦点管理绘图:

public class BorderTextBox : Panel {
  private Color _NormalBorderColor = Color.Gray;
  private Color _FocusBorderColor = Color.Blue;

  public TextBox EditBox;

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

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

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

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

3
使用OnPaint在控件上绘制自定义边框是可以的。但要知道如何使用OnPaint保持效率,并将渲染时间降至最低。如果您在使用自定义绘图例程时遇到GUI卡顿的情况,请阅读此内容:什么是在 .Net 应用程序中使用 OnPaint 的正确方式? 因为PraVn的被接受的答案可能看起来很简单,但实际上效率很低。像上面答案中发布的自定义控件一样使用会更好。
也许性能对您的应用程序不是问题,因为它很小,但对于具有大量自定义OnPaint例程的大型应用程序来说,使用PraVn所示的方式是错误的方法。

1

enter image description here

这是我完整的扁平化文本框控件,支持主题包括正常和聚焦状态下的自定义边框颜色。

该控件使用与Reza Aghaeihttps://dev59.com/i2kw5IYBdhLWcg3wm70h#38405319提到的相同概念,但FlatTextBox控件更具可定制性且无闪烁。

该控件以更好的方式处理WM_NCPAINT窗口消息,以帮助消除闪烁。

Protected Overrides Sub WndProc(ByRef m As Message)

    If m.Msg = WindowMessage.WM_NCPAINT AndAlso _drawBorder AndAlso Not DesignMode Then 'Draw the control border

        Dim w As Integer
        Dim h As Integer
        Dim clip As Rectangle
        Dim hdc As IntPtr

        Dim clientRect As RECT = Nothing
        GetClientRect(Handle, clientRect)

        Dim windowRect As RECT = Nothing
        GetWindowRect(Handle, windowRect)

        w = windowRect.Right - windowRect.Left
        h = windowRect.Bottom - windowRect.Top

        clip = New Rectangle(CInt((w - clientRect.Right) / 2), CInt((h - clientRect.Bottom) / 2), clientRect.Right, clientRect.Bottom)

        hdc = GetWindowDC(Handle)

        Using g As Graphics = Graphics.FromHdc(hdc)

            g.SetClip(clip, CombineMode.Exclude)

            Using sb = New SolidBrush(BackColor)
                g.FillRectangle(sb, 0, 0, w, h)
            End Using

            Using p = New Pen(If(Focused, _borderActiveColor, _borderNormalColor), BORDER_WIDTH)
                g.DrawRectangle(p, 0, 0, w - 1, h - 1)
            End Using

        End Using

        ReleaseDC(Handle, hdc)

        Return

    End If

    MyBase.WndProc(m)

End Sub

我已删除默认的BorderStyle属性,并用一个简单的布尔型DrawBorder属性替代它,该属性控制是否在控件周围绘制边框。

使用BorderNormalColor属性指定TextBox没有焦点时的边框颜色,使用BorderActiveColor属性指定控件接收焦点时的边框颜色。

FlatTextBox有两个主题:VS2019 DarkVS2019 Light,使用Theme属性在它们之间切换。

完整的FlatTextBox控件代码是用VB.NET编写的。 https://gist.github.com/ahmedosama007/37fe2004183a51a4ea0b4a6dcb554176


1
非常好,谢谢。这是我改进的建议: 剪辑应该像这样计算:var clientOrigin = new POINT(0, 0); clipOrigin = ClientToScreen(Handle, ref clientOrigin); new Rectangle( clientOrigin.Left - windowRect.Left, clientOrigin.Top - windowRect.Top, clientRect.Right, clientRect.Bottom )这涵盖了边框不对称的情况。有关ClientToScreen函数的更多信息: https://learn.microsoft.com/cs-cz/windows/win32/api/winuser/nf-winuser-clienttoscreen?redirectedfrom=MSDN - sebetovsky

0
With PictureBox1
    .Visible = False
    .Width = TextBox1.Width + 4
    .Height = TextBox1.Height + 4
    .Left = TextBox1.Left - 2
    .Top = TextBox1.Top - 2
    .SendToBack()
    .Visible = True
End With

虽然这段代码可能回答了问题,但提供有关它如何以及/或为什么解决问题的附加上下文将改善答案的长期价值。 - Donald Duck

0
将文本框的边框样式设置为“无”,然后在容器表单的“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 提供, 点击上面的
可以查看英文原文,
原文链接