在WM_PAINT中绘制的文本框在鼠标进入/离开时闪烁 [英] TextBox drawn in WM_PAINT flickers on mouse enter/leave

查看:199
本文介绍了在WM_PAINT中绘制的文本框在鼠标进入/离开时闪烁的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a custom TextBox in which I draw some place holder text when it's empty. It works pretty well, but it flickers when the mouse enters and leaves the TextBox. It seems related to the border becoming blue when the mouse hovers the control (I'm on Windows 8.1).

有什么主意我可以解决这个问题吗?

Any idea how I could fix this ?

我尝试了各种SetStyles标志,但均未成功.

I've tried various SetStyles flags without success.

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时遇到其他问题.这是我想出的最佳解决方案:


I had other problems with overriding OnPaint. Here is the best solution I came up with :

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,它可以是缓冲区,也可以是直接上下文,具体取决于样式.

Is there a special reason for using WM_PAINT instead of OnPaint? In WM_PAINT you obtain a drawing context from the handle, which is always a direct access to the control. In OnPaint you already have a Graphics in the event args, which can be either a buffer or a direct context, depending on the styles.

您提到您尝试了几种样式,但均未成功.首先,我会说尝试这些 并将您的绘制逻辑移至OnPaint:

You mentioned that you have tried a few styles with no success. Firstly I would say try these and move your paint logic into OnPaint:

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

如果它不起作用(聚焦的控件在Windows中的行为可能不正常),则必须坚持WM_PAINT,然后手动创建缓冲区.您的原始代码先绘制一个白色矩形,然后绘制一些文本,这会导致闪烁.您可以通过使用缓冲区来避免这种情况:

If it does not work (a focused control may behave strangely in Windows) and you must stick to WM_PAINT, then create a buffer manually. Your original code draws a white rectangle first, then some text, which causes flickering. You can avoid this by using a buffer:

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

这篇关于在WM_PAINT中绘制的文本框在鼠标进入/离开时闪烁的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆