玻璃上的渲染控制:find解决scheme,需要双缓冲/完善

我(终于!)find了一种方法来渲染Windows.Forms控件的玻璃上,似乎没有任何主要的缺点,也没有任何大的执行时间。 它的灵感来自Coded的这篇文章 ,它基本上解释了如何在本地重写绘制控件来绘制它们。

我使用这种方法将控件渲染到位图,并使用GDI +和适当的Alpha通道在NativeWindow的绘制区域上绘制。 实现很简单,但可以完善的可用性,但这不是这个问题的重点。 结果是相当令人满意的:

在玻璃上的真实文本框

但是,有两个方面需要修改才能真正使用。

  1. 双缓冲 ,因为这个叠加图像和真正的控制之间的闪烁频繁和可怕(用自己的代码testing自己)。 将基本控件设置为使用SetStyles(this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true)进行双缓冲SetStyles(this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true)不起作用,但是我怀疑我们可以使它稍微试用一下。
  2. 一些控件不起作用 。 我已经能够做出以下工作:

    • 文本框
    • MaskedComboBox
    • combobox(DropDownStyle == DropDownList)
    • 列表框
    • CheckedListBox
    • 列表显示
    • 树视图
    • 的DateTimePicker
    • 的MonthCalendar

    但我不能让这些工作,虽然我不明白为什么不。 我的教育猜测是,我引用整个控件的实际NativeWindow句柄,而我需要引用它的“input”(文本)部分,可能是一个孩子。 欢迎来自WinAPI专家的关于如何获得input窗口句柄的任何帮助。

    • combobox(DropDownStyle!= DropDownList)
    • 的NumericUpDown
    • RichTextBox的

但是修复双缓冲将是可用性的主要焦点

以下是一个示例用法:

 new GlassControlRenderer(textBox1); 

代码如下:

 public class GlassControlRenderer : NativeWindow { private Control Control; private Bitmap Bitmap; private Graphics ControlGraphics; protected override void WndProc(ref Message m) { switch (m.Msg) { case 0xF: // WM_PAINT case 0x85: // WM_NCPAINT case 0x100: // WM_KEYDOWN case 0x200: // WM_MOUSEMOVE case 0x201: // WM_LBUTTONDOWN this.Control.Invalidate(); base.WndProc(ref m); this.CustomPaint(); break; default: base.WndProc(ref m); break; } } public GlassControlRenderer(Control control) { this.Control = control; this.Bitmap = new Bitmap(this.Control.Width, this.Control.Height); this.ControlGraphics = Graphics.FromHwnd(this.Control.Handle); this.AssignHandle(this.Control.Handle); } public void CustomPaint() { this.Control.DrawToBitmap(this.Bitmap, new Rectangle(0, 0, this.Control.Width, this.Control.Height)); this.ControlGraphics.DrawImageUnscaled(this.Bitmap, -1, -1); // -1, -1 for content controls (eg TextBox, ListBox) } } 

我真的很高兴解决这个问题,并且一劳永逸地有一个真正的玻璃上渲染的方式,所有的.NET控件,没有WPF。

编辑:双缓冲/防闪烁的可能path:

  • 删除线this.Control.Invalidate()删除闪烁,但打破了文本框中的input。
  • 我已经尝试了WM_SETREDRAW方法和SuspendLayout方法,没有运气:

     [DllImport("user32.dll")] public static extern int SendMessage(IntPtr hWnd, Int32 wMsg, bool wParam, Int32 lParam); private const int WM_SETREDRAW = 11; public static void SuspendDrawing(Control parent) { SendMessage(parent.Handle, WM_SETREDRAW, false, 0); } public static void ResumeDrawing(Control parent) { SendMessage(parent.Handle, WM_SETREDRAW, true, 0); parent.Refresh(); } protected override void WndProc(ref Message m) { switch (m.Msg) { case 0xF: // WM_PAINT case 0x85: // WM_NCPAINT case 0x100: // WM_KEYDOWN case 0x200: // WM_MOUSEMOVE case 0x201: // WM_LBUTTONDOWN //this.Control.Parent.SuspendLayout(); //GlassControlRenderer.SuspendDrawing(this.Control); //this.Control.Invalidate(); base.WndProc(ref m); this.CustomPaint(); //GlassControlRenderer.ResumeDrawing(this.Control); //this.Control.Parent.ResumeLayout(); break; default: base.WndProc(ref m); break; } } 

这是一个闪烁less得多的版本,但仍然不完美。

 public class GlassControlRenderer : NativeWindow { private Control Control; private Bitmap Bitmap; private Graphics ControlGraphics; private object Lock = new object(); protected override void WndProc(ref Message m) { switch (m.Msg) { case 0x14: // WM_ERASEBKGND this.CustomPaint(); break; case 0x0F: // WM_PAINT case 0x85: // WM_NCPAINT case 0x100: // WM_KEYDOWN case 0x101: // WM_KEYUP case 0x102: // WM_CHAR case 0x200: // WM_MOUSEMOVE case 0x2A1: // WM_MOUSEHOVER case 0x201: // WM_LBUTTONDOWN case 0x202: // WM_LBUTTONUP case 0x285: // WM_IME_SELECT case 0x300: // WM_CUT case 0x301: // WM_COPY case 0x302: // WM_PASTE case 0x303: // WM_CLEAR case 0x304: // WM_UNDO base.WndProc(ref m); this.CustomPaint(); break; default: base.WndProc(ref m); break; } } private Point Offset { get; set; } public GlassControlRenderer(Control control, int xOffset, int yOffset) { this.Offset = new Point(xOffset, yOffset); this.Control = control; this.Bitmap = new Bitmap(this.Control.Width, this.Control.Height); this.ControlGraphics = Graphics.FromHwnd(this.Control.Handle); this.AssignHandle(this.Control.Handle); } public void CustomPaint() { this.Control.DrawToBitmap(this.Bitmap, new Rectangle(0, 0, this.Control.Width, this.Control.Height)); this.ControlGraphics.DrawImageUnscaled(this.Bitmap, this.Offset); // -1, -1 for content controls (eg TextBox, ListBox) } } 

我之前有一个闪烁的问题(窗体上的很多控件,用户控件)。 尝试几乎所有的东西。 这对我来说是有效的:

你有没有尝试把这个在你的表单类?

  protected override CreateParams CreateParams { get { CreateParams cp = base.CreateParams; cp.ExStyle |= 0x02000000; // WS_EX_COMPOSITED cp.ExStyle |= 0x00080000; // WS_EX_LAYERED return cp; } } 

而在你的构造函数中,你必须启用双缓冲,否则它将不起作用:

 this.DoubleBuffered = true; this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true); 

它只在启用航空时才起作用,否则会使闪烁变得更糟。

你也可以添加这个

  protected override CreateParams CreateParams { get { CreateParams cp = base.CreateParams; cp.ExStyle |= 0x02000000; // WS_EX_COMPOSITED return cp; } } 

到你的UserControls类。