2

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!

Felix
  • 71
  • 8
  • Do you have many controls on your panels? I've tried with empty panels and it's not flickering at all. – King King Nov 08 '13 at 13:40
  • I only have the splitter and the 2 panels on the form... yeah, thats very weird because i tried it at work on windows 7 and it works properly... but at home it doesnt – Felix Nov 08 '13 at 13:55
  • looks like it involves the OS here because I'm also using Windows 7, it works OK, maybe that's part of Windows 8 related problem. – King King Nov 08 '13 at 13:57

2 Answers2

4

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);
       }
    }
    
  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. – Dirk Feb 25 '14 at 11:12
  • Welcome to [so]. In accordance with [our behaviour guidelines](http://stackoverflow.com/help/behavior), please make sure that you disclose your affiliation with websites you've referred to in your posts. Thanks! – Qantas 94 Heavy Feb 25 '14 at 11:12
  • I am not sure how to disclose affiliation so I am removing the website link from my post. – Amit Bansal Jul 30 '15 at 06:11
  • I am making a tool similar to PowerToys FancyZones Layout Editor, and this solved the flickering of the SplitterPanels. – John Kurtz Aug 26 '22 at 19:54
0

Look at Splitter source code - http://referencesource.microsoft.com

  1. 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.

  2. 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.

  3. 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

Artem
  • 1,773
  • 12
  • 30