title: Problems with custom ScrollBar Delphi
body: <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>