Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/322.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在WM_PAINT中绘制的文本框在鼠标输入/离开时闪烁_C#_Winforms_Textbox_Gdi+_Wm Paint - Fatal编程技术网

C# 在WM_PAINT中绘制的文本框在鼠标输入/离开时闪烁

C# 在WM_PAINT中绘制的文本框在鼠标输入/离开时闪烁,c#,winforms,textbox,gdi+,wm-paint,C#,Winforms,Textbox,Gdi+,Wm Paint,我有一个自定义文本框,当它为空时,我在其中绘制一些占位符文本。 它工作得很好,但是当鼠标进入和离开文本框时,它会闪烁。这似乎与鼠标悬停在控件上时边框变蓝有关(我使用的是Windows 8.1) 你知道我该怎么解决这个问题吗 我尝试过各种不同的设置方式,但都没有成功 class MyTextBox : TextBox { public string PlaceHolder { get; set; } static readonly Brush sPlaceHolderBrush = ne

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

你知道我该怎么解决这个问题吗

我尝试过各种不同的设置方式,但都没有成功

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

TextBox包装本机编辑控件。Windows控件的老爹,可以追溯到1985年。为了让它在当时可用的非常有限的硬件上正常工作,它必须犯下非常严重的罪行。它不使用WM_油漆就可以自己绘制零件。30年的appcompat,它被以各种可以想象的方式黑客攻击,阻止了他们修复这个问题。你不能让它可靠,它是有效的!我在其他一些网站上读到,在文本框中使用OnPaint是不可能的。谢谢你证明他错了!将SetStyle放在OnHandleCreated而不是构造函数中可以避免此问题: