效果图:
无边框窗口的拖拽不多说了,直接StartWindowDrag就可以了。
无边框窗口的大小调整,在VCL下也有国外同行分享的一个很好的方案,但是FMX却没有人实现,VCL的代码拿过来没法直接用。
终于实现了,效果也还不错,当然希望有其他圈友有更好的方案,我这个就当抛砖引玉了。
unit Unit1; interface uses Winapi.Windows, Winapi.Messages, FMX.Platform.Win, System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, SizeGripLayout, FMX.Controls.Presentation, FMX.StdCtrls, FMX.Objects, FMX.Layouts; type TForm1 = class(TForm) Rectangle1: TRectangle; Button1: TButton; Button2: TButton; procedure Button1Click(Sender: TObject); procedure Rectangle1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single); private FOldWndPrc : TFNWndProc; FFormStub : Pointer; procedure CreateHandle; override; procedure DestroyHandle; override; procedure CustomWndProc(var aMessage: TMessage); protected public { Public declarations } end; var Form1: TForm1; implementation {$R *.fmx} {$R *.Windows.fmx MSWINDOWS} procedure TForm1.Button1Click(Sender: TObject); begin Close; end; procedure TForm1.CreateHandle; begin inherited CreateHandle; var eStyle := GetWindowLong(FmxHandleToHWND(Handle), GWL_STYLE); SetWindowLong(FmxHandleToHWND(Handle), GWL_STYLE, eStyle or WS_THICKFRAME); FFormStub := MakeObjectInstance(CustomWndProc); FOldWndPrc := TFNWndProc(SetWindowLongPtr(FmxHandleToHWND(Handle), GWLP_WNDPROC, NativeInt(FFormStub))); end; procedure TForm1.CustomWndProc(var aMessage: TMessage); const RESIZE_BORDER = 8; var P: TPointF; Hit: Integer; begin if aMessage.Msg = WM_NCHITTEST then begin P.X := SmallInt(LoWord(aMessage.LParam)); P.Y := SmallInt(HiWord(aMessage.LParam)); P := ScreenToClient(P); Hit := 0; if (P.X >= 0) and (P.X < RESIZE_BORDER) and (P.Y >= 0) and (P.Y < RESIZE_BORDER) then Hit := HTTOPLEFT else if (P.X >= ClientWidth - RESIZE_BORDER) and (P.X < ClientWidth) and (P.Y >= 0) and (P.Y < RESIZE_BORDER) then Hit := HTTOPRIGHT else if (P.X >= 0) and (P.X < RESIZE_BORDER) and (P.Y >= ClientHeight - RESIZE_BORDER) and (P.Y < ClientHeight) then Hit := HTBOTTOMLEFT else if (P.X >= ClientWidth - RESIZE_BORDER) and (P.X < ClientWidth) and (P.Y >= ClientHeight - RESIZE_BORDER) and (P.Y < ClientHeight) then Hit := HTBOTTOMRIGHT else if (P.Y >= 0) and (P.Y < RESIZE_BORDER) then Hit := HTTOP else if (P.Y >= ClientHeight - RESIZE_BORDER) and (P.Y < ClientHeight) then Hit := HTBOTTOM else if (P.X >= 0) and (P.X < RESIZE_BORDER) then Hit := HTLEFT else if (P.X >= ClientWidth - RESIZE_BORDER) and (P.X < ClientWidth) then Hit := HTRIGHT; if Hit <> 0 then begin aMessage.Result := Hit; Exit; end; end; aMessage.Result := CallWindowProc(FOldWndPrc, FmxHandleToHWND(Handle), aMessage.Msg, aMessage.wParam, aMessage.lParam); end; procedure TForm1.DestroyHandle; begin SetWindowLongPtr(FmxHandleToHWND(Handle), GWLP_WNDPROC, NativeInt(FOldWndPrc)); FreeObjectInstance(FFormStub); inherited DestroyHandle; end; procedure TForm1.Rectangle1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single); begin Self.StartWindowDrag; end; end.
升级单元如下:
unit Winapi.FormResizeHelper; interface uses System.Types, System.SysUtils, System.Classes, System.UITypes, FMX.Forms, FMX.Platform.Win, Winapi.Windows, Winapi.Messages; type TFormResizeHelper = class( TObject ) private FHandle : HWND; FForm : TForm; FResizeWidth: Integer; FOldWndProc : TFNWndProc; FFormStub : Pointer; procedure CustomWndProc( var aMessage: TMessage ); public constructor Create( AForm: TForm; const AResizeWidth: Integer = 8 ); destructor Destroy; override; end; implementation { TFormResizeHelper } constructor TFormResizeHelper.Create( AForm: TForm; const AResizeWidth: Integer ); var LStyle: LongInt; begin inherited Create; FForm := AForm; FResizeWidth := AResizeWidth; if not Assigned( FForm ) then Exit; // Usa a função segura para obter o HWND FHandle := FormToHWND( FForm ); if FHandle = 0 then Exit; // Adiciona o estilo que permite redimensionamento LStyle := GetWindowLong( FHandle, GWL_STYLE ); SetWindowLong( FHandle, GWL_STYLE, LStyle or WS_THICKFRAME ); // Subclassing: Substitui o WndProc para interceptar as mensagens FFormStub := MakeObjectInstance( CustomWndProc ); FOldWndProc := TFNWndProc( SetWindowLongPtr( FHandle, GWLP_WNDPROC, NativeInt( FFormStub ) ) ); end; destructor TFormResizeHelper.Destroy; begin if FHandle <> 0 then begin // Restaura o WndProc original SetWindowLongPtr( FHandle, GWLP_WNDPROC, NativeInt( FOldWndProc ) ); // Libera a instância do nosso WndProc FreeObjectInstance( FFormStub ); end; inherited Destroy; end; procedure TFormResizeHelper.CustomWndProc( var aMessage: TMessage ); var P : TPointF; LHitTest: Integer; begin if aMessage.Msg = WM_NCHITTEST then begin P := FForm.ScreenToClient( TPointF.Create( SmallInt( LoWord( aMessage.LParam ) ), SmallInt( HiWord( aMessage.LParam ) ) ) ); LHitTest := 0; // Lógica para detectar se o mouse está nas bordas ou no topo if ( P.X >= 0 ) and ( P.X < FResizeWidth ) then LHitTest := HTLEFT else if P.X >= FForm.ClientWidth - FResizeWidth then LHitTest := HTRIGHT; if ( P.Y >= 0 ) and ( P.Y < FResizeWidth ) then begin if LHitTest = HTLEFT then LHitTest := HTTOPLEFT else if LHitTest = HTRIGHT then LHitTest := HTTOPRIGHT else LHitTest := HTTOP; end else if P.Y >= FForm.ClientHeight - FResizeWidth then begin if LHitTest = HTLEFT then LHitTest := HTBOTTOMLEFT else if LHitTest = HTRIGHT then LHitTest := HTBOTTOMRIGHT else LHitTest := HTBOTTOM; end else if LHitTest = 0 then LHitTest := HTCLIENT; // Retorna para a area do cliente // Permite arrastar o formulário clicando em qualquer lugar da área do cliente if LHitTest = HTCLIENT then aMessage.Result := HTCAPTION else if LHitTest <> 0 then begin aMessage.Result := LHitTest; Exit; end; end; // aMessage.Result := CallWindowProc( FOldWndProc, FHandle, aMessage.Msg, aMessage.wParam, aMessage.LParam ); end; end. ---------- uses Winapi.FormResizeHelper; ... procedure TForm1.Button1Click( Sender: TObject ); begin BorderStyle := TFmxFormBorderStyle.None; // testing... // TFormResizeHelper.Create( Self ); // apply changes... end;
还没有评论,来说两句吧...