There is nothing wrong with your television set. Do not attempt to adjust the picture.
Recently someone asked me what splitter component I had used in my (very secret) resource editor. The splitter is a very simple descendant of the standard VCL TSplitter
. The only difference from the standard one is that my custom splitter sports a grab bar, bumps, knobs or whatever they are called.
The source for the custom splitter is only about 40 lines of code so I thought I might as well just post it here and let everyone benefit from it. The source also demonstrates a very useful technique that I often use to customize the standard VCL components without the hassle of having to create design time packages.
It’s really very simple so without further ado here comes:
unit amSplitter; // ----------------------------------------------------------------------------- // TSplitter enhanced with grab bar // The original author is Anders Melander, anders@melander.dk, http://melander.dk // Copyright © 2008 Anders Melander // ----------------------------------------------------------------------------- // License: // Creative Commons Attribution-Share Alike 3.0 Unported // http://creativecommons.org/licenses/by-sa/3.0/ // ----------------------------------------------------------------------------- interface uses ExtCtrls; //------------------------------------------------------------------------------ // // TSplitter enhanced with grab bar // //------------------------------------------------------------------------------ type TSplitter = class(ExtCtrls.TSplitter) protected procedure Paint; override; end; implementation uses Windows, Graphics, Controls, Classes; //------------------------------------------------------------------------------ // // TSplitter enhanced with grab bar // //------------------------------------------------------------------------------ procedure TSplitter.Paint; var R: TRect; X, Y: integer; DX, DY: integer; i: integer; Brush: TBitmap; begin R := ClientRect; Canvas.Brush.Color := Color; Canvas.FillRect(ClientRect); X := (R.Left+R.Right) div 2; Y := (R.Top+R.Bottom) div 2; if (Align in [alLeft, alRight]) then begin DX := 0; DY := 3; end else begin DX := 3; DY := 0; end; dec(X, DX*2); dec(Y, DY*2); Brush := TBitmap.Create; try Brush.SetSize(2, 2); Brush.Canvas.Brush.Color := clBtnHighlight; Brush.Canvas.FillRect(Rect(0,0,1,1)); Brush.Canvas.Pixels[0, 0] := clBtnShadow; for i := 0 to 4 do begin Canvas.Draw(X, Y, Brush); inc(X, DX); inc(Y, DY); end; finally Brush.Free; end; end; end.
TSplitter
on a form.amSplitter
to the interface section uses clause.
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, amSplitter;
Notice that I do not register the custom TSplitter
class as a component via the Register()
design time function. Instead I rely on a handy trick that allows me to use the standard TSplitter
at design-time, but use my custom TSplitter
at run-time.
The first part of the trick is that the custom component class name must be the same as the component we want to replace:
type TSplitter = class(ExtCtrls.TSplitter)
The second part of the trick is to reference the unit where the custom splitter is declared after the unit where the standard splitter is declared. TSplitter
is declared in the ExtCtrls
unit and our replacement is declared in the amSplitter
unit, so:
interface uses ExtCtrls, amSplitter;
The net effect of this is that when the form that contains a TSplitter
component is streamed in from the DFM resource, the streaming system will instantiate a copy of the custom TSplitter
instead of the standard TSplitter
.
I most often use the above technique when I have found a bug in the VCL. This enables me to fix the problem without patching the VCL source.
For test purposes and for custom components that are only used on one form you don’t even need to put the code in a separate unit. Just declare the custom class in the same unit as the form, but before the form is declared.
For example try the following:
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls; type // Declare the custom TPanel class *before* the form is declared TPanel = class(ExtCtrls.TPanel) public constructor Create(AOwner: TComponent); override; end; TForm1 = class(TForm) Panel1: TPanel; private protected public end; implementation {$R *.dfm} constructor TPanel.Create(AOwner: TComponent); begin inherited Create(AOwner: TComponent); Color := clRed; end;
Easy, huh?
Great tip, thank you.
Nice trick!
A neat trick indeed. One that I used back in Delphi 7 to "silently" add Vista fixes to "TForm" - all I had to do was add my "Deltics.Forms" unit to the uses list of any unit containing a TForm. By convention I always list any 3rd party units AFTER standard VCL units (with my own project units listed after those) so scoping took care of itself.The other thing I did with this trick was to add some useful additional features to the "Application" object, courtesy of some unit variables hidden behind my own TApplication "facade".The details of my trickery are here: http://www.deltics.co.nz/blog/?p=222
Thanks, nice trick. Something else I noticed.. Cool skin, what is it?
Skin? What do you mean?
Great trick.
Do you have some more VCL enhancements you could share with the world?
I've got plenty. Unfortunately I don't have much time to clean them up for general consumption and release, but I'll post some more when time permits.
Nice example of an interceptor class.
Great trick! I'll like it. And of course thanks for splitter I ask!
This is cool, thanks. I'm hoping to do something to make snap to min size and restore on hover over as well on click - when time allows. Interested if anyone has already done same.
The standard
TSplitter
already supports snap (AutoSnap
andMinSize
properties), but what do you mean by "restore on hover"?Thanks for the idea.
We used it in our project.
Hi Anders,A bit off-topic, but do you have any idea if there's an easy fix for Splitter resize flickering ?Example:If you set the splitter controls color to, say, clGray, and resize the control, it'll result in a lot of flickering.
Good question.
I'm assuming you are talking about the flicker caused by resizing the splitter's parent control - not flicker caused by moving the splitter. The latter can be eliminated with simple double buffering and the other usual techniques (e.g. set
FullRepaint
=False andParentBackGround
=False).I've tried many times to find a solution to this problem (I just wasted all of today revisiting the problem) but always unsuccessful.
The flicker isn't of the traditional kind; The flicker I'm seeing with
TSplitter
is, as far as I have determined, caused by the way the VCL resizes aligned controls. What it should do is to resize and reposition all the controls in one go and then let them repaint. What I'm seeing is that child controls are resized, repositioned and repainted during the process. This causes flicker as some of the controls are moved, resized and repainted multiple times when the size of their parent changes.Nice trick but it does not work on Delphi2010!