I am trying to draw a coloured border around the client area of a custom control with scrollbars. To this end, I set BorderWidth
to a positive integer and respond to the WM_NCPAINT
message. This sounds like mixing VCL and Win32, but the BorderWidth
property simply results in an appropriate handling of the WM_NCCALCSIZE
message.
The following code is a SSCCE:
unit Unit6;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TSample = class(TCustomControl)
protected
procedure Paint; override;
procedure CreateParams(var Params: TCreateParams); override;
procedure WMNCPaint(var Message: TWMNCPaint); message WM_NCPAINT;
published
property BorderWidth;
end;
TForm6 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form6: TForm6;
implementation
{$R *.dfm}
{ TSample }
procedure TSample.CreateParams(var Params: TCreateParams);
begin
inherited;
Params.Style := Params.Style or WS_VSCROLL or WS_HSCROLL;
end;
procedure TSample.Paint;
begin
inherited;
Canvas.Brush.Color := clWhite;
Canvas.FillRect(ClientRect);
end;
procedure TSample.WMNCPaint(var Message: TWMNCPaint);
var
dc: HDC;
R: TRect;
begin
DefaultHandler(Message);
dc := GetWindowDC(Handle);
try
Brush.Color := clYellow;
GetWindowRect(Handle, R);
with R do
R := Rect(0, 0, Right - Left, Bottom - Top);
ExcludeClipRect(dc, BorderWidth, BorderWidth,
R.Right - BorderWidth, R.Bottom - BorderWidth);
FillRect(dc, R, Brush.Handle);
finally
ReleaseDC(Handle, dc);
end;
end;
procedure TForm6.FormCreate(Sender: TObject);
begin
with TSample.Create(self) do
begin
Parent := Self;
SetBounds(10, 10, 500, 100);
BorderWidth := 10;
end;
end;
end.
The results looks as follows:
This looks perfect except for the bottom-right square. That area is easily fixed by doing something about it; I deliberately do not paint this area because it has nothing to do with the actual problem I am trying to describe. So just ignore that square, please.
Now, I am able to resize the form by dragging the right border of it. I first make it smaller, so that the vertical scrollbar of the sample control window is hidden. Then I slowly enlarge the form so that the sample control is again fully visible. Then it looks like this:
Here you can see the problem: the ~BorderSize
left-most pixels of the vertical scrollbar are seemingly not painted by the operating system.
Some observations:
- Using a full
inherited
instead of a mereDefaltHandler(Message)
makes the problem much worse. In this case, the yellow area will occlude the scrollbars entirely after the form has been moved temporarily off-screen and after a control-occluding form shrink-grow operation.
Implementing a matching response to the
WM_NCHITTEST
message makes the control behave in a better way, but does not resolve the scrollbar painting issue.I am aware of How to draw a custom border inside the non client area of a control with scroll bars?; the answers to this Q all suffer from the issues described above.
I am using Delphi 2009 and Windows 7 Home Premium, 64-bit, Aero enabled.