在WinForm中.NET Framework提供的TextBox是不能修改边框的颜色的,在如今对界面要求越来越高的情况下只有黑色边框的TextBox显然不能满人们的需求。本文将介绍一种修改TextBox边框颜色且不会闪烁的最佳做法。
最开始的时候,我还是像重绘其他控件一样重写TextBox的OnPaint()方法,结果发现其根本就不走OnPaint()方法。因为有些控件是由系统进程绘制的,重写OnPaint()方法不起作用,TextBox就是其中之一。
面对以上问题,网上也有很多答案,自己都不太满意,但总体只有两种做法:
1. 隐藏TextBox的边框并将其放在一个容器中,设置容器的背景颜色来达到边框的效果。
2. 重写WndProc()方法,拦截系统消息来绘制自己想要颜色的边框。
这两种方法都有缺陷:
1. 每个TextBox都多一个容器控件,当TextBox较多的时候,加载较慢且浪费系统资源。
2. 鼠标在TextBox上来回滑动或者是点击TextBox的时候,会闪烁。
那么有没有一种方法既不占用系统资源又不闪烁呢?答案是有的。
我们只需要重写TextBox的CreateParams属性,给其加上一个边框,然后绘制自己想要的即可。以下是代码:
绘制过程中会用到一些API和消息常量等先声明
public class NativeMethods
{
internal const int WS_EX_CLIENTEDGE = 512 /*0x0200*/;
internal const int WS_EX_WINDOWEDGE = 0x0100;
internal const int WM_PAINT = 15; // 0x000f
internal const int WM_NCPAINT = 133; // 0x0085
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
public RECT(Rectangle rect)
{
this.bottom = rect.Bottom;
this.left = rect.Left;
this.right = rect.Right;
this.top = rect.Top;
}
public RECT(int left, int top, int right, int bottom)
{
this.bottom = bottom;
this.left = left;
this.right = right;
this.top = top;
}
public static RECT FromXYWH(int x, int y, int width, int height)
{
return new RECT(x, y, x + width, y + height);
}
public int Width
{
get
{
return this.right - this.left;
}
}
public int Height
{
get
{
return this.bottom - this.top;
}
}
public override /*Object*/ string ToString()
{
return String.Concat(
"Left = ",
this.left,
" Top ",
this.top,
" Right = ",
this.right,
" Bottom = ",
this.bottom);
}
public static implicit operator Rectangle(RECT rect)
{
return Rectangle.FromLTRB(rect.left, rect.top, rect.right, rect.bottom);
}
}
[DllImport("user32.dll")]
internal static extern bool GetWindowRect(IntPtr hwnd, ref RECT lpRect);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
internal static extern IntPtr CreateRectRgn(int x1, int y1, int x2, int y2);
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
internal static extern IntPtr GetWindowDC(IntPtr hWnd);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
internal static extern IntPtr CreateCompatibleDC(IntPtr hDC);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
internal static extern IntPtr CreateCompatibleBitmap(IntPtr hDC, int width, int height);
[DllImport("gdi32", CharSet = CharSet.Auto, ExactSpelling = true)]
internal static extern IntPtr SelectObject(IntPtr hdc, IntPtr hObject);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
internal static extern int CombineRgn(IntPtr hRgn, IntPtr hRgn1, IntPtr hRgn2, int nCombineMode);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
public static extern int OffsetRgn(IntPtr hrgn, int nXOffset, int nYOffset);
[DllImport("gdi32")]
internal static extern bool DeleteObject(IntPtr hObject);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
internal static extern int SelectClipRgn(IntPtr hDC, IntPtr hRgn);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
public static extern int ExcludeClipRect(IntPtr hdc, int nLeft, int nTop, int nRight, int nBottom);
[DllImport("gdi32.dll")]
internal static extern bool BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, Int32 dwRop);
[DllImport("gdi32", EntryPoint = "DeleteDC", CharSet = CharSet.Auto, ExactSpelling = true)]
internal static extern bool DeleteDC(IntPtr hDC);
}
添加一个组件CustomTextBox继承TextBox,实现自定义边框颜色
public partial class CustomTextBox : System.Windows.Forms.TextBox
{
private Rectangle m_rcClient = Rectangle.Empty;
private Color _coBorder = Color.Red;
public CustomTextBox()
{
InitializeComponent();
}
protected override CreateParams CreateParams
{
get
{
CreateParams cparams = base.CreateParams;
BorderStyle border = this.BorderStyle;
if (!(border == BorderStyle.Fixed3D))
{
cparams.ExStyle &= ~NativeMethods.WS_EX_CLIENTEDGE;
cparams.Style &= ~8388608;
switch (border)
{
// Unlike other controls, text box doesn't draw its single border in the NC area!
case BorderStyle.Fixed3D:
case BorderStyle.FixedSingle:
// 使一个视窗具有凹陷边框
cparams.ExStyle = cparams.ExStyle | NativeMethods.WS_EX_CLIENTEDGE | NativeMethods.WS_EX_WINDOWEDGE;
break;
}
}
return cparams;
}
}
public Color BorderColor
{
get { return this._coBorder; }
set { this._coBorder = value; }
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
}
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case NativeMethods.WM_PAINT:
this.OnWmNcPaint(ref m);
break;
case NativeMethods.WM_NCPAINT:
this.OnWmNcPaint(ref m);
return;
}
base.WndProc(ref m);
}
private void OnWmNcPaint(ref Message m)
{
NativeMethods.RECT rcWnd = new NativeMethods.RECT();
NativeMethods.GetWindowRect(m.HWnd, ref rcWnd);
int intWidth = rcWnd.Width;
int intHeight = rcWnd.Height;
int width = rcWnd.Width;
int height = rcWnd.Height;
IntPtr hRgn = NativeMethods.CreateRectRgn(0, 0, width, height);
if (hRgn != IntPtr.Zero)
{
IntPtr hDc = NativeMethods.GetWindowDC(m.HWnd);
if (hDc != IntPtr.Zero)
{
IntPtr memDc = NativeMethods.CreateCompatibleDC(hDc);
if (memDc != IntPtr.Zero)
{
IntPtr hBmp = NativeMethods.CreateCompatibleBitmap(hDc, width, height);
if (hBmp != IntPtr.Zero)
{
IntPtr hObj = NativeMethods.SelectObject(memDc, hBmp);
if (m.WParam.ToInt32() != 1)
{
IntPtr tmpRgn = NativeMethods.CreateRectRgn(0, 0, 0, 0);
NativeMethods.CombineRgn(tmpRgn, m.WParam, IntPtr.Zero, NativeMethods.RGN_COPY);
NativeMethods.OffsetRgn(tmpRgn, -rcWnd.left, -rcWnd.top);
NativeMethods.CombineRgn(hRgn, hRgn, tmpRgn, NativeMethods.RGN_AND);
NativeMethods.DeleteObject(tmpRgn);
}
using (Graphics g = Graphics.FromHdc(memDc))
{
RenderNCArea(g, width, height, true);
}
NativeMethods.SelectClipRgn(hDc, hRgn);
NativeMethods.ExcludeClipRect(hDc, m_rcClient.X, m_rcClient.Y, m_rcClient.Right, m_rcClient.Bottom);
NativeMethods.BitBlt(hDc, 0, 0, rcWnd.Width, rcWnd.Height, memDc, 0, 0, 0x00CC0020/*SRCCOPY*/ );
NativeMethods.SelectObject(memDc, hObj);
NativeMethods.DeleteObject(hBmp);
}
NativeMethods.DeleteDC(memDc);
}
NativeMethods.DeleteDC(hDc);
}
IntPtr hTmp = NativeMethods.CreateRectRgn(m_rcClient.Left, m_rcClient.Top, m_rcClient.Right, m_rcClient.Bottom);
if (hTmp != IntPtr.Zero)
{
NativeMethods.CombineRgn(hRgn, hRgn, hTmp, NativeMethods.RGN_AND);
NativeMethods.OffsetRgn(hRgn, rcWnd.left, rcWnd.top);
NativeMethods.DeleteObject(hTmp);
Message msg = new Message();
msg.Msg = m.Msg;
msg.HWnd = m.HWnd;
msg.WParam = hRgn;
base.WndProc(ref msg);
}
NativeMethods.DeleteObject(hRgn);
}
m.Result = IntPtr.Zero;
}
private void RenderNCArea(Graphics g, int width, int height, bool bDrawBorder)
{
Rectangle rc = new Rectangle(0, 0, width, height);
using (Brush brush = new SolidBrush(this.BackColor))
{
g.FillRectangle(brush, 0, 0, width, height);
}
if (bDrawBorder)
{
DrawBorders(rc, g);
}
}
private void DrawBorders(Rectangle rc, Graphics g)
{
ControlPaint.DrawBorder(g, rc, this.BorderColor, ButtonBorderStyle.Solid);
}
}
效果如图:
本文章由创风网原创,转载请注明出处:http://www.windite.com/article/details/ftw0yz5