3

When a window is resized I want to handle the OnResize event when resizing is finished as updating the graphic takes seconds. That is tricky because resizing a window generates lots of resize events. As updating the window takes a while I don't want the window being updated on each event. I tried to detect a mouse up to flag that as the event that finishes a resize but the mouseup is never detected.

TLama had a nice solution but alas, that's VCL, I need it for Firemonkey. Any suggestions for FMX?

Community
  • 1
  • 1
Arnold
  • 4,016
  • 5
  • 42
  • 82

3 Answers3

1

What about something similar to the Debounce() or Throttle() function in Underscore.js? Both functions provide ways to limit how regularly a procedure is executed.

Shannon Matthews
  • 7,816
  • 4
  • 38
  • 69
1

Here is a way to handle it in FMX on Windows, you need to change your form to inherit from TResizeForm and assign OnResizeEnd property. Not very clean since it depends on on FMX internals, but should work.

unit UResizeForm;

interface

uses
  Winapi.Windows,
  System.SysUtils, System.Classes,
  FMX.Types, FMX.Forms, FMX.Platform.Win;

type
  TResizeForm = class(TForm)
  strict private
    class var FHook: HHook;
  strict private
    FOnResizeEnd: TNotifyEvent;
  public
    property OnResizeEnd: TNotifyEvent read FOnResizeEnd write FOnResizeEnd;
    class constructor Create;
    class destructor Destroy;
  end;

implementation

uses Winapi.Messages;

var
  WindowAtom: TAtom;

function Hook(code: Integer; wparam: WPARAM; lparam: LPARAM): LRESULT stdcall;
var
  cwp: PCWPSTRUCT;
  Form: TForm;
  ResizeForm: TResizeForm;
begin
  try
    cwp := PCWPSTRUCT(lparam);
    if cwp.message = WM_EXITSIZEMOVE then
    begin
      if WindowAtom <> 0 then
      begin
        Form := TForm(GetProp(cwp.hwnd, MakeIntAtom(WindowAtom)));
        if Form is TResizeForm then
        begin
          ResizeForm := Form as TResizeForm;
          if Assigned(ResizeForm.OnResizeEnd) then
          begin
            ResizeForm.OnResizeEnd(ResizeForm);
          end;
        end;
      end;
    end;
  except
    // eat exception
  end;
  Result := CallNextHookEx(0, code, wparam, lparam);
end;

class constructor TResizeForm.Create;
var
  WindowAtomString: string;
begin
  WindowAtomString := Format('FIREMONKEY%.8X', [GetCurrentProcessID]);
  WindowAtom := GlobalFindAtom(PChar(WindowAtomString));
  FHook := SetWindowsHookEx(WH_CALLWNDPROC, Hook, 0, GetCurrentThreadId);
end;

class destructor TResizeForm.Destroy;
begin
  UnhookWindowsHookEx(FHook);
end;

end.
EugeneK
  • 1,942
  • 1
  • 17
  • 16
0

As a workaround add a TTimer to the form, set its initial state to disabled and the Interval property to 100 ms. To minimize the amount of times your application reacts to the OnResize event use the following code:

procedure TForm1.FormResize(Sender: TObject);
begin
    with ResizeTimer do
    begin
        Enabled := false;
        Enabled := true;
    end;
end;

procedure TForm1.ResizeTimerTimer(Sender: TObject);
begin
    ResizeTimer.Enabled := false;

    // Do stuff after resize
end;

The minimal delay should not be noticeable by the user.

Olaf Hess
  • 1,338
  • 9
  • 17