How to create a TListBox that is resizable at runtime #114

Question
How can I let my user resize a TListBox (using the mouse) at runtime, like the way we do at design time in Delphi?

Here's a sample listbox control that can be resized at runtime. It has two new properties: AllowResize (True by default), and ResizeBorder (shows a resizing border if True). You can still resize the box if ResizeBorder is False (as long as AllowResize is True), but only the standard listbox border is displayed. Since the control can now be resized, the Constaints property is also published.

unit ResizeListBox;

interface

uses
  Windows, Messages, SysUtils, Classes, Controls, StdCtrls;

type
  TResizeListBox = class(TListBox)
  private
    FAllowResize: Boolean;
    FResizeBorder: Boolean;
    procedure SetAllowResize(const Value: Boolean);
    procedure SetResizeBorder(const Value: Boolean);
    procedure WMNCHitTest(var Msg: TWMNCHitTest); message WM_NCHITTEST;
  protected
    procedure CreateParams(var Params: TCreateParams); override;
  public
    constructor Create(AOwner: TComponent); override;
  published
    property AllowResize: Boolean
      read FAllowResize write SetAllowResize default True;
    property ResizeBorder: Boolean
      read FResizeBorder write SetResizeBorder default False;
    property Constraints;
  end;

implementation

{ TResizeListBox }

constructor TResizeListBox.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FAllowResize := True;
end;

procedure TResizeListBox.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  if FAllowResize then
    Params.WindowClass.style := Params.WindowClass.style
      or (CS_VREDRAW + CS_HREDRAW);
  if FResizeBorder then
    Params.Style := Params.Style or WS_THICKFRAME;
end;

procedure TResizeListBox.SetAllowResize(const Value: Boolean);
begin
  FAllowResize := Value;
  RecreateWnd;
end;

procedure TResizeListBox.SetResizeBorder(const Value: Boolean);
begin
  FResizeBorder := Value;
  RecreateWnd;
end;

procedure TResizeListBox.WMNCHitTest(var Msg: TWMNCHitTest);
var
  P: TPoint;
begin
  inherited;
  if FAllowResize then
  begin
    if (not FResizeBorder) and (Msg.Result = HTBORDER) then
    begin
      {Cursor is on the normal border - determine where}
      P := Parent.ScreenToClient(Point(Msg.XPos, Msg.YPos));
      if PtInRect(Rect(Left, Top, Left + 8, Top + 8), P) then
        Msg.Result := HTTOPLEFT
      else
      if PtInRect(
        Rect(Left + Width - 8, Top, Left + Width, Top + 8), P
      ) then
        Msg.Result := HTTOPRIGHT
      else if PtInRect(
        Rect(Left, Top + Height - 8, Left + 8, Top + Height), P
      ) then
        Msg.Result := HTBOTTOMLEFT
      else if PtInRect(
        Rect(
          Left + Width - 8, Top + Height - 8, Left + Width, Top + Height
        ),
        P
      ) then
        Msg.Result := HTBOTTOMRIGHT
      else if PtInRect(Rect(Left, Top, Left + 8, Top + Height), P) then
        Msg.Result := HTLEFT
      else if PtInRect(Rect(Left, Top, Left + Width, Top + 8), P) then
        Msg.Result := HTTOP
      else if PtInRect(
        Rect(Left + Width - 8, Top, Left + Width, Top + Height), P
      ) then
        Msg.Result := HTRIGHT
      else
        Msg.Result := HTBOTTOM;
    end;
  end
  else if Msg.Result in [
    HTBOTTOM, HTBOTTOMLEFT, HTBOTTOMRIGHT, HTLEFT,
    HTRIGHT, HTTOP, HTTOPLEFT, HTTOPRIGHT
  ] then
    Msg.Result := HTBORDER;  {Prevent resize if no AllowResize is False}
end;

end.
Original resource: The Delphi Pool
Author: Yorai Aminov
Added: 2009/10/26
Last updated: 2009/10/26