[Estimated Reading Time: 3 minutes]

One control that I have not seen in the line-up of new controls in the VCL for Tiburón is a size grip.

Most people may not even realise that this control exists, even though it is part of Windows itself and is really quite useful.  Fortunately, implementing it ourselves is truly trivial.

What is a Size Grip?

A size grip is the small triangular control, usually in the lower right hand corner of a window that provides an indication that the window is resizable and is also a grab-point that the user can use to resize the window itself.

You will see one if you place a status-bar control on a resizable form:

Example of a Size Grip
Example of a Size Grip

If the form is not resizable, the grip does not appear in the status bar.

But what if I have a form that I want to be sizable, but which does not have a status bar, doesn’t need a status bar, and which perhaps in all other respects doesn’t even look resizable?

Imagine, for example, that I have redesigned a dialog box so that it now needs to be resizable….

If you look at the dialog box shown in that link, you will see that it is resizable but offers no immediately obvious visual cue that this is in fact the case.  It clearly doesn’t need a status bar.

What would be useful in such cases is a way to add a size grip without having to use a status bar.

And this is exactly what a size grip control does for us.

The Implementation

As I mentioned, the implementation of such a control is trivial.  So trivial that the entire implementation is provided below.  No need for a download in this case.

interface

  uses
    Classes,
    Controls;

  type
    TSizeGrip = class(TWinControl)
    protected
      procedure CreateParams(var Params: TCreateParams); override;
      procedure CreateWnd; override;
    public
      constructor Create(aOwner: TComponent); override;
    end;

implementation

  uses
    Windows;

{ TSizeGrip }

  constructor TSizeGrip.Create(aOwner: TComponent);
  begin
    inherited;

    ControlStyle := [csOpaque, csFixedWidth, csFixedHeight];

    Anchors := [akRight, akBottom];
    Cursor  := crSizeNWSE;
    Height  := 11;
    Width   := 11;
  end;

  procedure TSizeGrip.CreateParams(var Params: TCreateParams);
  begin
    inherited;
    CreateSubClass(Params, 'SCROLLBAR');
    Params.Style := Params.Style or WS_CLIPSIBLINGS or SBS_SIZEGRIP;
  end;

  procedure TSizeGrip.CreateWnd;
  begin
    inherited;
    Left := Parent.ClientWidth - Width - 1;
    Top  := Parent.ClientHeight - Height - 1;
    SendToBack;
  end;

As you can see, a size grip is in fact a special form of the built in SCROLLBAR Windows control class.

This code should work in any Win32 version of Delphi.  In my case since I use Delphi 7 I use dotted unit names and have it implemented in Deltics.Controls.SizeGrip.  In earlier Delphi versions you will need to use a unit name with no dots.

And yes, we should probably use system metrics to size the control.

The implementation has the control place itself in the lower right hand corner of it’s parent, and stay there. But how do we ensure that we only show the control if the form is resizable?

Well, actually, we don’t need to worry about that. Simply placing the control on a form makes the form resizable (via the size grip) even if that form would normally not be resizable.  That is, if the user moves their mouse over the size grip it will change to the resizing cursor and they can resize the form by grabbing the control and dragging.

So the user can see that the dialog is resizable and is provided with the means to resize the form, even if the form has no border at all, such as this one, with border style bsNone:

Resizable, Borderless Form
Resizable, Borderless Form

Getting this is as easy as adding:

    with TSizeGrip.Create(self) do
      Parent := self;

To a form constructor or OnCreate event.

In fact, in my applications I can simply write in any form constructor or OnCreate event:

    HasSizeGrip := TRUE;

Any form.

How is that possible?  HasSizeGrip is not a property of TForm, so where does it come from?

I’m saving that for next time.  😉

6 thoughts on “Resizing a Non-Resizable Form”

  1. I was actually thinking about this yesterday! Thanks for saving me from doing the research!!! 😀

  2. Nice work! With a bit of investigation, though, I’ve found you can in fact get away with even less code *and* respect the system’s default size:

    type
    TSizeGrip = class(TWinControl)
    protected
    procedure CreateParams(var Params: TCreateParams); override;
    public
    constructor Create(AOwner: TComponent); override;
    end;

    constructor TSizeGrip.Create(AOwner: TComponent);
    begin
    inherited;
    ControlStyle := [csOpaque, csFixedWidth, csFixedHeight];
    Anchors := [akRight, akBottom];
    Cursor := crSizeNWSE;
    end;

    procedure TSizeGrip.CreateParams(var Params: TCreateParams);
    var
    R: TRect;
    begin
    inherited;
    CreateSubClass(Params, ‘SCROLLBAR’);
    Params.Style := Params.Style or WS_CLIPSIBLINGS or SBS_SIZEGRIP or
    SBS_SIZEBOXBOTTOMRIGHTALIGN;
    if not Windows.GetClientRect(Params.WndParent, R) then
    RaiseLastOSError;
    Params.X := R.Left;
    Params.Y := R.Top;
    Params.Width := R.Right – R.Left;
    Params.Height := R.Bottom – R.Top;
    end;

    The default VCL code will update the control’s Left, Top, Width and Height properties after it calls CreateWindowEx.

  3. @Peter – Glad to have been of service. 🙂 Be sure to check out Chris’ very useful revisions in his comments!

    @CR – Looks like more or less the same amount of code to me. 😉

    But one less override and respect for system defaults is all good. Thanks for the revisions!

  4. Slight deviation: I have always been amused by the resize grip in Microsoft Access forms. They are there, you can see them, but they are non-functional!

    To resize an Access form, you have to grab the border of the form itself; something that works with the remaining three corners of ANY resizable form.

  5. Jolyon, firstly thanks for the nice component. 🙂

    Also, if you want to add this component to the component palette then the following additional functions and methods will 1. register the component; 2. allow the sizegrip to work within the IDE; and 3. overcome inadvertent moving or resizing at design time …

    type
    TSizeGrip = class(TWinControl)
    protected
    procedure CMDesignHitTest(var Message: TCMDesignHitTest); message CM_DESIGNHITTEST;
    procedure CreateParams(var Params: TCreateParams); override;
    public
    constructor Create(aOwner: TComponent); override;
    procedure SetBounds(ALeft, ATop, AWidth, AHeight: Integer); override;
    end;

    procedure Register;

    implementation

    procedure Register;
    begin
    RegisterComponents(‘Samples’, [TSizeGrip]);
    end;

    procedure TSizeGrip.CMDesignHitTest(var Message: TCMDesignHitTest);
    begin
    Message.Result := 1;
    end;

    procedure TSizeGrip.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
    begin
    if (csDesigning in ComponentState) and assigned(Parent) then
    begin
    ALeft := Parent.ClientWidth – width;
    ATop := Parent.ClientHeight – height;
    AWidth := width;
    AHeight := height;
    end;
    inherited;
    end;

    //the following are implemented as per CR’s suggestions above.
    constructor TSizeGrip.Create(aOwner: TComponent);
    begin

    end;

    procedure TSizeGrip.CreateParams(var Params: TCreateParams);
    begin

    end;

Comments are closed.