I have the following problem: I placed a splitter-control (not split-container) in my form and added 2 panels. The splitter works properly but when I move the splitter, it starts to flicker - the panels dont.
I get the same result with a Split-Container.
I tried this but nothing works
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.DoubleBuffer, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.DoubleBuffered = true;
...
class XSplitter : Splitter
{
public XSplitter() : base()
{
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.DoubleBuffer, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.DoubleBuffered = true;
}
}
class XPanel : Panel
{
public XPanel() : base()
{
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.DoubleBuffer, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.DoubleBuffered = true;
}
}
I use Windows 8.1 and VS 2010
Thx for help!
Here are the steps to use this control:
- Add new class "NonFlickerSplitContainer" to your C# application.
- Replace auto generated class code with C# code shown below.
Use NonFlickerSplitContainer object instead of SplitContainer object in your application.
public partial class NonFlickerSplitContainer : SplitContainer
{
public NonFlickerSplitContainer()
{
this.SetStyle(ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint |
ControlStyles.OptimizedDoubleBuffer, true);
MethodInfo objMethodInfo = typeof(Control).GetMethod("SetStyle",BindingFlags.NonPublic|BindingFlags.Instance);
object[] objArgs = new object[] { ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint |
ControlStyles.OptimizedDoubleBuffer, true };
objMethodInfo.Invoke(this.Panel1, objArgs);
objMethodInfo.Invoke(this.Panel2, objArgs);
}
}
Look at Splitter source code - http://referencesource.microsoft.com
Splitter draws on parents' (not own) device context, using GDI calls from outside of WM_PAINT:
private void DrawSplitHelper(int splitSize) {
...
IntPtr parentHandle = ParentInternal.Handle;
IntPtr dc = UnsafeNativeMethods.GetDCEx(new HandleRef(ParentInternal, parentHandle), NativeMethods.NullHandleRef, NativeMethods.DCX_CACHE | NativeMethods.DCX_LOCKWINDOWUPDATE);
IntPtr halftone = ControlPaint.CreateHalftoneHBRUSH();
...
so style setting does not have any effect.
When Splitter moves, it removes image from previous position before drawing at new place:
private void DrawSplitBar(int mode) {
if (mode != DRAW_START && lastDrawSplit != -1) {
DrawSplitHelper(lastDrawSplit);
lastDrawSplit = -1;
...
If the screen refreshes at this moment, you see flickering.
Windows Forms was developed at those years, when most people used CRT monitors, and halftone brushes (vide supra) looks smooth. These days some LCD monitors flicker even on static picture.
The solution is creating of solid brush instead of halftone one:
var brush = default(LOGBRUSH);
brush.lbColor = 0x2A2A2A; // Invert alternate bits except highest: ..#.#.#.
and redrawing only difference rectangle on moving:
private void DrawSplitBar(int mode)
{
...
if (mode != DRAW_END)
{
var rect = newRect;
SubtractRect(out newRect, ref newRect, ref oldRect);
SubtractRect(out oldRect, ref oldRect, ref rect);
}
DrawSplitHelper(oldRect);
...
See my solution at https://gist.github.com/ArtemAvramenko/e260420b86564cf13d2e