I have the following code:
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);
}
}
This is working perfect, but it doesn't show the text until it gets focus.
Can anybody help me? What is wrong?
Thank in advance.
You have to draw text manually as well.
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);
}
Alternatively you can try to use e.Graphics.DrawString()
method if TextRenderer
is not giving you desired results (I always have better results with this approach thou).
To change border color of TextBox
you can override WndProc
method and handle WM_NCPAINT
message. Then get the window device context of the control using GetWindowDC
because we want to draw to non-client area of control. Then to draw, it's enough to create a Graphics
object from that context, then draw border for control.
To redraw the control when the BorderColor
property changes, you can use RedrawWindow
method.
Code
Here is a TextBox
which has a BorderColor
property. The control uses BorderColor
if the property values is different than Color.Transparent
and BorderStyle
is its default value Fixed3d
.
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);
}
}
Result
Here is the result using different colors and different states. All states of border-style is supported as you can see in below image and you can use any color for border:
Download
You can clone or download the working example:
- Download Zip
- Github repository
There are several ways to do this and none are ideal. This is just the nature of WinForms. However, you have some options. I will summarise:
One way you can achieve what you want is by embedding a TextBox
in a Panel
as follows.
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; }
}
}
You can also do this without overriding any controls, but the above method is better. The above will draw a border when the control gets focus. if you want the border on permanently, set the FocusedAlways
property to True
.
I hope this helps.
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);
}