Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi TSplitter flickering issue

I am using Delphi with VCL styles enabled and I would like to change the color of the TSplitter for my form. I override TSplitter.Paint in an interposer class to paint a darker color than the default VCL styles cBtnFace color, but there is significant flickering on the form when resized. Is there a way to remove this flickering?

I've tried these things to try and reduce the flickering, but none have worked:

  1. Disabling VCL styles (TSplitter.StyleElements := []).

  2. Changing the VCL Styles Bitmap Style Designer's object element for "Splitter," but modifying this object element doesn't change the splitter's appearance.

  3. Trying to process the WM_ERASEBKGND message on TControl objects, but I was unable to get the procedure to be called in my interposer class.

    procedure WMEraseBkgnd(var Msg: TWMEraseBkgnd); message WM_ERASEBKGND;
    ...
    procedure TSplitter.WMEraseBkgnd(var Msg: TWMEraseBkgnd);
    begin
        // this is never invoked by the TSplitter
        Msg.Result := 1;
    end;
    

Any other ideas for getting rid of the flickering on a TSplitter? There's no DoubleBuffer property for TSplitter or anything like that from what I can tell.

UPDATE

Unfortunately, I can't share the codebase, but I can tell you that this is the way the application UI is setup when the TSplitter flickers:

TForm (DoubleBuffered = False)
-> BackgroundPanel (DoubleBuffered = True, ParentBackground = False)
-> -> A TGradient, image and label to fill the BackgroundPanel
-> LeftPanel (ParentBackground = False`, no flickering)
-> -> LeftPanelFrame and frame content (selective double buffering)
-> TSplitter
-> RightPanel (ParentBackground = False, no flickering)
-> -> RightPanelFrame and frame content (selective double buffering)

There is also a toolbar and a main menu at the top of the form, but the rest of the UI components are set to alClient (or OnResized to fill the space).

I assumed that since the BackgroundPanel is behind the LeftPanel, TSplitter, and RightPanel (i.e, Control -> Send to Back), the DoubleBuffered = True and ParentBackground = False on the BackgroundPanel would help reduce/remove the flicker from any components on an immediate UI layer in front of it (i.e, the TSplitter). But, this doesn't seem to be the case.

Perhaps I'll try placing a TPanel as a parent of LeftPanel, TSplitter, and RightPanel and set its DoubleBuffered = True and ParentBackground = False. I'll have to try that later and get back. So, it would look like this:

TForm
-> BackgroundPanel (DoubleBuffered = True, ParentBackground = False)
-> -> A TGradient, image and label to fill the BackgroundPanel
-> EncapsulatingPanel (DoubleBuffered = True, ParentBackground = False)
-> -> LeftPanel (ParentBackground = False)
-> -> -> LeftPanelFrame and frame content
-> -> TSplitter
-> -> RightPanel (ParentBackground = False)
-> -> -> RightPanelFrame and frame content

Lastly, I should note that double buffering the TForm substantially slows down the UI when resizing (a black trail on the right of the window), not when doing other UI things when the app is not resizing.

UPDATE 2

Unfortunately, while my approach above (creating a background parent TPanel) fixed the flickering on the TSplitter, it also caused other weird UI issues, perhaps some of which @David Heffernan alluded to in the comments. For now, I've just left the flickering issue, since the splitter is only 1px wide and only flickers on width + height resizing.

like image 241
spurgeon Avatar asked Oct 05 '22 12:10

spurgeon


1 Answers

The TSplitter control may not have a DoubleBuffered property, but this property should be implemented on the upper-most control of where you experience flicker - in most cases, the form. As long as all the controls have ParentDoubleBuffered still enabled as default, they will all also acquire this change.

DoubleBuffered doesn't always fix flicker, and in some situations can make things worse. You may consider only using DoubleBuffered during resizing, and then switch it back off when resizing is done. This property should only be used on those controls which are actually being resized. For example, when resizing the form, enable DoubleBuffered on the form. But when resizing just the contents of a panel, enable it just for that panel (and all its child controls).

As for the performance drop of your application due to this change, you may also consider implementing a flag when you're resizing which will pause any program execution which might bog it down (anything that redraws such as labels, images, etc.). This its self may also be the solution to your problem as well (actually could be a second answer).

like image 103
Jerry Dodge Avatar answered Oct 10 '22 03:10

Jerry Dodge