Scrollbar painting issue with thickened non-client window border

829 Views Asked by At

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:

Screenshot

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:

Screenshot

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:

  1. Using a full inherited instead of a mere DefaltHandler(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.

Screenshot

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

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

0

There are 0 best solutions below