Note that there are some explanatory texts on larger screens.

plurals
  1. PORendering controls on glass: Solution found, needs double-buffering/perfecting
    text
    copied!<p>I (finally!) found a way of rendering Windows.Forms controls on glass that doesn't seem to have any major drawback nor any big implementation time. It's inspired by <a href="http://www.codedblog.com/2007/09/17/owner-drawing-a-windowsforms-textbox/" rel="nofollow noreferrer">this article</a> from Coded, which basically explains how to natively override the painting of controls to draw over them.</p> <p>I used that approach to render the control to a bitmap and paint it back with GDI+ and the appropriate alpha channel over the NativeWindow's painting area. The implementation is simple but could be perfected for usability, but that's not the point of this question. The results are, however, quite satisfying:</p> <p><img src="https://i.stack.imgur.com/Izep5.png" alt="Real textbox on glass"></p> <p>There are 2 areas that need to be fixed for this to be really usable, however. </p> <ol> <li><strong>Double-buffering</strong>, because the flicker between this overlay image and the real control is frequent and horrible (test yourself with the code). Setting the basic control to be double buffered with <code>SetStyles(this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true)</code> doesn't work, but I suspect we can make it work with a little trial and error.</li> <li><p><strong>Some controls don't work</strong>. I've been able to make the following work:</p> <ul> <li>TextBox</li> <li>MaskedComboBox</li> <li>ComboBox (DropDownStyle == DropDownList)</li> <li>ListBox</li> <li>CheckedListBox</li> <li>ListView</li> <li>TreeView</li> <li>DateTimePicker</li> <li>MonthCalendar</li> </ul> <p>But I can't get these to work, although I don't see why not. My educated guess is that the actual NativeWindow handle I'm referencing the whole control, while I need to reference the "input" (textual) part of it, probably a child. Any help from WinAPI experts on how to get that input window handle is welcome.</p> <ul> <li>ComboBox (DropDownStyle != DropDownList)</li> <li>NumericUpDown</li> <li>RichTextBox</li> </ul></li> </ol> <p>But fixing the double buffering would be the <strong>main focus</strong> for usability.</p> <p>Here's a sample usage:</p> <pre><code>new GlassControlRenderer(textBox1); </code></pre> <p>Here's the code:</p> <pre><code>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 (e.g. TextBox, ListBox) } } </code></pre> <p>I'd be really glad to fix this, and <strong>once and for all</strong> have a real way of rendering on glass, for all .NET controls, without WPF.</p> <p>EDIT: Possible paths for double-buffering/anti-flicker:</p> <ul> <li>Removing the line <code>this.Control.Invalidate()</code> removes the flicker, but breaks the typing in a textbox. </li> <li><p>I've tried the WM_SETREDRAW approach and the SuspendLayout method, with no luck:</p> <pre><code>[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; } } </code></pre></li> </ul>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload