31

How can I change the BorderColor of the Textbox when a user Clicks on it or focuses on it?

Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
Raggy Shrestha
  • 435
  • 1
  • 5
  • 8
  • If you are looking for a `TextBox` having `BorderColor` property, take a look at [Change border color in TextBox](http://stackoverflow.com/a/39420512/3110834) – Reza Aghaei Apr 13 '17 at 16:41

8 Answers8

47

You can handle WM_NCPAINT message of TextBox and draw a border on the non-client area of control if the control has focus. You can use any color to draw border:

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);
            }
        }
    }
}

Result

The painting of borders while the control is focused is completely flicker-free:

Change TextBox border color on focus

BorderColor property for TextBox

In the current post I just change the border color on focus. You can also add a BorderColor property to the control. Then you can change border-color based on your requirement at design-time or run-time. I've posted a more completed version of TextBox which has BorderColor property: in the following post:

Change Textbox border color

Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
  • How to do the same for buttons when their flat style is standard? I mean buttons get a blue highlight when clicked. – Simple Nov 13 '21 at 18:56
  • I tried your solution, it works great except it flickers, too. Perhaps I need `BufferedGraphics` for double buffering? – 8749236 Sep 17 '22 at 16:55
  • @8749236 did you try my other answer as well? https://stackoverflow.com/questions/17466067/change-border-color-in-textbox-c-sharp/39420512#39420512 ? If you try and it flickers as well, let me know you OS and your .NET version, and I'll try to fix it. – Reza Aghaei Sep 17 '22 at 20:01
  • @8749236 Check my answer https://stackoverflow.com/a/74106039/5514131 that offers a better flicker-free custom TextBox control based on Reza Aghaei code – Ahmed Osama Oct 18 '22 at 05:32
  • @AhmedOsama Thank you for your offer, I currently don't have time to work on this. Also the demo gif shows your window has input lag, which does not suit my needs, but it does look interesting and may be helpful when I get back to this. – 8749236 Oct 20 '22 at 02:31
  • @8749236 The GIF is low quality and intended for demo purposes only so it wouldn't be wise to judge the control performance from it – Ahmed Osama Oct 22 '22 at 16:47
20

try this

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();
}
Dennis Meissel
  • 1,825
  • 1
  • 21
  • 33
PraveenVenu
  • 8,217
  • 4
  • 30
  • 39
  • thanks man , it does work , but it also changes the borderColor of all other textboxes in the Form too , Can you explain how did it happen and how to make the color red ,its only in Blue !! – Raggy Shrestha Mar 20 '12 at 04:13
  • I do not think the above code will change the border of all textboxes. What we are doing is, we draw a rectangle around the textBox1 – PraveenVenu Mar 20 '12 at 04:17
  • How to Apply Different Colors on the Border , or around the Rectangle – Raggy Shrestha Mar 20 '12 at 06:41
  • i made some changes to @PraVn code and now i think it is pretty : int variance = 1; g.DrawRectangle( p,new Rectangle( textBox1.Location.X - variance,textBox1.Location.Y - variance,textBox1.Width + variance,textBox1.Height +variance ) ); variance = 2; g.DrawRectangle( p,new Rectangle( textBox1.Location.X - variance,textBox1.Location.Y - variance,textBox1.Width +variance+1,textBox1.Height +variance+1) ); – Arash Oct 30 '12 at 18:13
  • 6
    It is a bad idea, concerning performance, to have anything done in the OnPaint, when having more controls in the parent control. To see for yourself what I mean, do the following: place 10 controls on the form, but a breakpoint on `Form1_Paint`. Now the border is drawn around `textBox1` for every control being painted on Form1. It is better to create a custom control, inherited from TextBox, to make sure the border is only drawn once, when needed. Instead of drawing alot of times which is unnecessary and increases render time drastically. Especially with more custom onpaint job likes this. – Mike de Klerk Nov 30 '12 at 19:47
11

This is an ultimate solution to set the border color of a TextBox:

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);
    }
}
Balazs Tihanyi
  • 6,659
  • 5
  • 23
  • 24
5

WinForms was never good at this and it's a bit of a pain.

One way you can try is by embedding a TextBox in a Panel and then manage the drawing based on focus from there:

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);
  }
}
LarsTech
  • 80,625
  • 14
  • 153
  • 225
3

Using OnPaint to draw a custom border on your controls is fine. But know how to use OnPaint to keep efficiency up, and render time to a minimum. Read this if you are experiencing a laggy GUI while using custom paint routines: What is the right way to use OnPaint in .Net applications?

Because the accepted answer of PraVn may seem simple, but is actually inefficient. Using a custom control, like the ones posted in the answers above is way better.

Maybe the performance is not an issue in your application, because it is small, but for larger applications with a lot of custom OnPaint routines it is a wrong approach to use the way PraVn showed.

Community
  • 1
  • 1
Mike de Klerk
  • 11,906
  • 8
  • 54
  • 76
1

enter image description here

Here is my complete Flat TextBox control that supports themes including custom border colors in normal and focused states.

The control uses the same concept mentioned by Reza Aghaei https://stackoverflow.com/a/38405319/5514131 ,however the FlatTextBox control is more customizable and flicker-free.

The control handles the WM_NCPAINT window message in a better way to help eliminate flicker.

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

I have removed the default BorderStyle property and replaced it with a simple boolean DrawBorder property that controls whether to draw a border around the control or not.

Use the BorderNormalColor property to specify the border color when the TextBox has no focus, and the BorderActiveColor property to specify the border color when the control receives focus.

The FlatTextBox comes with two themes VS2019 Dark and VS2019 Light, use the Theme property to switch between them.

Complete FlatTextBox control code written in VB.NET https://gist.github.com/ahmedosama007/37fe2004183a51a4ea0b4a6dcb554176

Ahmed Osama
  • 348
  • 1
  • 9
  • 1
    Great, thank you. Here is my suggestion to improve: The clip should be calculated like this: 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 ) This covers the case when the border is asymetric. More about the ClientToScreen function: https://learn.microsoft.com/cs-cz/windows/win32/api/winuser/nf-winuser-clienttoscreen?redirectedfrom=MSDN – sebetovsky Mar 22 '23 at 16:00
0

set Text box Border style to None then write this code to container form "paint" event

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);
}
Moory Pc
  • 860
  • 15
  • 16
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
  • 8,409
  • 22
  • 75
  • 99
user3314233
  • 43
  • 1
  • 1
  • While this code may answer the question, providing additional context regarding how and/or why it solves the problem would improve the answer's long-term value. – Donald Duck Dec 05 '20 at 20:16