鼠标进入/离开时WM_PAINT中绘制的TextBox闪烁

我有一个自定义的TextBox,当它为空时我在其中绘制一些占位符文本。 它运行得很好,但是当鼠标进入和离开TextBox时它会闪烁。 当鼠标hover在控件上时(我在Windows 8.1上),它似乎与边框变成蓝色有关。

知道如何解决这个问题吗?

我尝试了各种SetStyles标志但没有成功。

class MyTextBox : TextBox { public string PlaceHolder { get; set; } static readonly Brush sPlaceHolderBrush = new SolidBrush(Color.FromArgb(70, 70, 78)); static readonly StringFormat sFormat = new StringFormat { Alignment = StringAlignment.Near, LineAlignment = StringAlignment.Center }; private Font mPlaceHolderFont; [DllImport("user32")] private static extern IntPtr GetWindowDC(IntPtr hwnd); protected override void WndProc(ref Message m) { base.WndProc(ref m); if (m.Msg == 0x0F) { if (string.IsNullOrEmpty(Text) && !Focused) { IntPtr dc = GetWindowDC(Handle); using (Graphics g = Graphics.FromHdc(dc)) { if (mPlaceHolderFont == null) mPlaceHolderFont = new Font(Font, FontStyle.Italic); var rect = new RectangleF(2, 2, Width - 4, Height - 4); g.FillRectangle(Brushes.White, rect); g.DrawString(PlaceHolder, mPlaceHolderFont, sPlaceHolderBrush, rect, sFormat); } } } } } 

我在覆盖OnPaint时遇到了其他问题。 这是我提出的最佳解决方案:

 class MyTextBox : TextBox { public string PlaceHolder { get; set; } static readonly Brush sPlaceHolderBrush = new SolidBrush(Color.FromArgb(70, 70, 78)); static readonly StringFormat sFormat = new StringFormat { Alignment = StringAlignment.Near, LineAlignment = StringAlignment.Near }; private Font mPlaceHolderFont; private Brush mForegroundBrush; protected override void OnHandleCreated(EventArgs e) { base.OnHandleCreated(e); SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true); } protected override void OnPaint(PaintEventArgs e) { var bounds = new Rectangle(-2, -2, Width, Height); var rect = new RectangleF(1, 0, Width - 2, Height - 2); e.Graphics.FillRectangle(Brushes.White, rect); if (string.IsNullOrEmpty(Text) && !Focused) { if (mPlaceHolderFont == null) mPlaceHolderFont = new Font(Font, FontStyle.Italic); if (mForegroundBrush == null) mForegroundBrush = new SolidBrush(ForeColor); e.Graphics.DrawString(PlaceHolder, mPlaceHolderFont, sPlaceHolderBrush, rect, sFormat); } else { var flags = TextFormatFlags.Default | TextFormatFlags.TextBoxControl; if (!Multiline) flags |= TextFormatFlags.SingleLine | TextFormatFlags.NoPadding; TextBoxRenderer.DrawTextBox(e.Graphics, bounds, Text, Font, flags, TextBoxState.Selected); } } } 

是否有使用WM_PAINT而不是OnPaint的特殊原因? 在WM_PAINT中,您可以从句柄获取绘图上下文,它始终是对控件的直接访问。 在OnPaint您已经在事件args中有一个Graphics ,它可以是缓冲区或直接上下文,具体取决于样式。

你提到过你尝试过几种风格却没有成功。 首先我会说尝试这些并将你的绘画逻辑移动到OnPaint

 SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true); 

如果它不起作用(焦点控件可能在Windows中表现奇怪)并且你必须坚持WM_PAINT,然后手动创建一个缓冲区。 您的原始代码首先绘制一个白色矩形,然后绘制一些文本,这会导致闪烁。 您可以通过使用缓冲区来避免这种情况:

 IntPtr dc = GetWindowDC(Handle); using (Graphics g = Graphics.FromHdc(dc)) { // creating a buffered context using (BufferedGraphicsContext context = new BufferedGraphicsContext()) { // creating a buffer for the original Graphics using (BufferedGraphics bg = context.Allocate(e.Graphics, ClientRectangle)) { if (mPlaceHolderFont == null) mPlaceHolderFont = new Font(Font, FontStyle.Italic); var gBuf = bg.Graphics; var rect = ClientRectangle; rect.Inflate(-1, -1); gBuf.FillRectangle(Brushes.White, rect); gBuf.DrawString(PlaceHolder, mPlaceHolderFont, sPlaceHolderBrush, rect, sFormat); // copying the buffer onto the original Graphics bg.Render(e.Graphics); } } }