Problems with custom ScrollBar Delphi
<p>I try to implement custom scrollbar painting in standard TMemo. For this I intercepted WM_NCXXX messages and now I can draw buttons and gauge of scrollbar (only vertical, though).<br>
But a day ago I have been stuck with two problems:<br>
1. Where should I do calculations to prepare information for thumb painting? In <code>WM_NCCALCSIZE</code>?<br>
2. I can't get how to make necessary calculations to be able to move thumb by mouse as in standard scrollbar?<br>
Information for thumb painting I will get via <a href="https://msdn.microsoft.com/ru-ru/library/windows/desktop/bb787583%28v=vs.85%29.aspx" rel="nofollow noreferrer">GetScrollInfo</a> function, it is not so difficult.  </p>

<p>At the picture below you can see what I've already done. Left scrollbar is a system scrollbar, right one - custom scrollbar.</p>

<p><a href="https://i.stack.imgur.com/2srw7.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/2srw7.png" alt="control example"></a></p>

<p>There full code to create control similar on picture.</p>

<pre><code>unit MemoEx;

interface

uses
  Windows,
  Messages,
  SysUtils,
  Classes,
  Graphics,
  Controls,
  Forms,
  StdCtrls,
  ExtCtrls,
  StrUtils,
  Dialogs,
  ClipBrd;


type
  TMemoEx = class(TCustomMemo)
  private
    { Private declarations }
    FVertBar: Boolean;

    FPressedBtn1,
    FPressedBtn2,
    FSelectedBtn1,
    FSelectedBtn2: Boolean;

    FBarBmp,
    FBtn1Bmp,
    FBtn2Bmp: TBitmap;
    MainDC: hDC;

  protected
    { Protected declarations }
    procedure WMGETDLGCODE(var Message: TWMGETDLGCODE); message WM_GETDLGCODE;
    procedure WMNCCALCIZE(var Message: TWMNCCALCSIZE); message WM_NCCALCSIZE;
    procedure WMNCPAINT(var Message: TMessage); message WM_NCPAINT;
    procedure WMNCHITTEST(var Message: TMessage); message WM_NCHITTEST;
    procedure WMNCMOUSEMOVE(var Message: TMessage); message WM_NCMOUSEMOVE;
    procedure WMNCLBUTTONDOWN(var Message: TMessage); message WM_NCLBUTTONDOWN;
    procedure WMNCLBUTTONUP(var Message: TMessage); message WM_NCLBUTTONUP;

    procedure Change; override;

    procedure PaintScrollBarVert(DC: hDC);
    procedure PaintButtonVert1(DC: hDC);
    procedure PaintButtonVert2(DC: hDC);
    function CursorPosition(AControl: TControl): TPoint;

  public
    { Public declarations }
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

  published
    { new properties }
    property WordWrap;
    property Lines;
    property ScrollBars;
  end;


implementation


constructor TMemoEx.Create(AOwner: TComponent);
begin
  Inherited Create(AOwner);
  Width := 100;
  Height := 80;
  Constraints.MinWidth := 0;
  Constraints.MinHeight := 0;
  AutoSize := false;
  DoubleBuffered := false;

  FSelectedBtn1 := false;
  FSelectedBtn2 := false;
  FPressedBtn1 := false;
  FPressedBtn2 := false;

  FBarBmp := TBitmap.Create;
  FBtn1Bmp := TBitmap.Create;
  FBtn2Bmp := TBitmap.Create;
end;

destructor TMemoEx.Destroy;
begin
  FBarBmp.Free;
  FBtn1Bmp.Free;
  FBtn2Bmp.Free;

  Inherited Destroy;
end;

procedure TMemoEx.WMGETDLGCODE(var Message: TWMGETDLGCODE);
begin
  Inherited;
  Message.Result := Message.Result or DLGC_WANTTAB or DLGC_WANTALLKEYS;
end;

procedure TMemoEx.WMNCCALCIZE(var Message: TWMNCCALCSIZE);
begin
  Inherited;

  // decrease width to create non-client area
  Dec(Message.CalcSize_Params.rgrc[0].Right, 17);
  FVertBar := true;
end;

procedure TMemoEx.WMNCPAINT(var Message: TMessage);
begin
  Inherited;
  MainDC := GetWindowDC(Handle);

  if FVertBar then
    PaintScrollBarVert(MainDC);

  if FVertBar then
  begin
    PaintButtonVert1(MainDC);
    PaintButtonVert2(MainDC);
  end;

  ReleaseDC(Handle, MainDC);
end;

procedure TMemoEx.WMNCHITTEST(var Message: TMessage);
begin
  Inherited;
end;

procedure TMemoEx.WMNCMOUSEMOVE(var Message: TMessage);
begin
  Inherited;

  // top vert button
  if PtInRect(Rect(Width - 17 - 2, ClientRect.Top, Width - 2, 17), CursorPosition(Self)) then
    FSelectedBtn1 := true
  else
    FSelectedBtn1 := false;

  // bottom vert button
  if PtInRect(Rect(Width - 17 - 2, ClientRect.Bottom - 17, Width - 2, ClientRect.Bottom + 17), CursorPosition(Self)) then
    FSelectedBtn2 := true
  else
    FSelectedBtn2 := false;

  Perform(WM_NCPAINT, 1, 0);
end;

procedure TMemoEx.WMNCLBUTTONDOWN(var Message: TMessage);
begin
  Inherited;

  // top vert button
  if PtInRect(Rect(Width - 17 - 2, ClientRect.Top, Width, 17), CursorPosition(Self)) then
    FPressedBtn1 := true;

  // top vert button
  if PtInRect(Rect(Width - 17 - 2, ClientRect.Bottom - 17, Width, ClientRect.Bottom + 17), CursorPosition(Self)) then
    FPressedBtn2 := true;

  Perform(WM_NCPAINT, 1, 0);
end;

procedure TMemoEx.WMNCLBUTTONUP(var Message: TMessage);
begin
  Inherited;

  FPressedBtn1 := false;
  FPressedBtn2 := false;
  Perform(WM_NCPAINT, 1, 0);
end;

procedure TMemoEx.Change;
begin
  Inherited Changed;
  Perform(WM_NCPAINT, 1, 0);
end;

procedure TMemoEx.PaintScrollBarVert(DC: hDC);
begin
  FBarBmp.Width := 17;
  FBarBmp.Height := ClientRect.Bottom;

  FBarBmp.Canvas.Brush.Color := clLime;
  FBarBmp.Canvas.FillRect(FBarBmp.Canvas.ClipRect);
  BitBlt(MainDC, Width - 17 - 2, ClientRect.Top + 2, FBarBmp.Width, FBarBmp.Height, FBarBmp.Canvas.Handle, 0, 0, SRCCOPY);
end;

procedure TMemoEx.PaintButtonVert1(DC: hDC);
begin
  FBtn1Bmp.Width := 17;
  FBtn1Bmp.Height := 17;

  if not FSelectedBtn1 then
    FBtn1Bmp.Canvas.Brush.Color := clRed;
  if FSelectedBtn1 then
    FBtn1Bmp.Canvas.Brush.Color := clBlue;
  if FSelectedBtn1 and FPressedBtn1 then
    FBtn1Bmp.Canvas.Brush.Color := clPurple;

  FBtn1Bmp.Canvas.FillRect(FBtn1Bmp.Canvas.ClipRect);
  BitBlt(DC, Width - 17 - 2, ClientRect.Top + 2, FBtn1Bmp.Width, FBtn1Bmp.Height, FBtn1Bmp.Canvas.Handle, 0, 0, SRCCOPY);
end;

procedure TMemoEx.PaintButtonVert2(DC: hDC);
begin
  FBtn2Bmp.Width := 17;
  FBtn2Bmp.Height := 17;

  if not FSelectedBtn2 then
    FBtn1Bmp.Canvas.Brush.Color := clRed;
  if FSelectedBtn2 then
    FBtn1Bmp.Canvas.Brush.Color := clBlue;
  if FSelectedBtn2 and FPressedBtn2 then
    FBtn1Bmp.Canvas.Brush.Color := clPurple;

  FBtn1Bmp.Canvas.FillRect(FBtn1Bmp.Canvas.ClipRect);
  BitBlt(DC, Width - 17 - 2, ClientRect.Bottom - 17 + 2, FBtn1Bmp.Width, FBtn1Bmp.Height, FBtn1Bmp.Canvas.Handle, 0, 0, SRCCOPY);
end;

function TMemoEx.CursorPosition(AControl: TControl): TPoint;
begin
  GetCursorPos(Result);
  Result := AControl.ScreenToClient(Result);
end;


end.
</code></pre>

<p>P.S.
Top and bottom buttons are interactive)</p>

<p>Thanks anyone for any suggestions!</p>