Firemonkey Form always restores to wsNormal - delphi

I don't know if this could be a BUG or just some configuration.
By setting the form to WndowState = wsMaximized and pressing Windows+D (Show Desktop), when the form is restored from the taskbar the WindowState changes to WndowState = wsNormal.
This only happens in Firemonkey, in VCL it doesn't.
Would there be any solution to keep the form always in wsMaximized?

I think this ought to be reported as a bug.
But I think I have a workaround to this. When the form is hidden the OnDeactivate event is fired. Of course that is also fired if another form takes the focus. But if SetBounds() is then called without OnActivate being fired first, as far as I've been able to tell, this only happens when the form is being shown again from the Show Desktop state.
So this code seems, from the testing I've done, to resolve the issue:
type
TForm1 = class(TForm)
procedure FormActivate(Sender: TObject);
procedure FormDeactivate(Sender: TObject);
private
WasMaximised: Boolean;
public
procedure SetBounds(ALeft, ATop, AWidth, AHeight: Integer); override;
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
procedure TForm1.FormActivate(Sender: TObject);
begin
WasMaximised:=False;
end;
procedure TForm1.FormDeactivate(Sender: TObject);
begin
WasMaximised:=WindowState=TWindowState.wsMaximized;
end;
procedure TForm1.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
begin
inherited;
if WasMaximised then begin
WasMaximised:=False;
WindowState:=TWindowState.wsNormal;
WindowState:=TWindowState.wsMaximized;
end;
end;

Related

Delphi drag and drop files from Windows explorer to TListView does not work

I'm using Delphi 10.3 Community Edition. I'm trying to drag and drop files from a Windows folder onto my application but the Windows message handler is not called when I drag and drop a file on the form.
This is what I have at the moment:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
public
{ Public declarations }
protected
procedure WMDropFiles(var Msg: TWMDropFiles); message WM_DROPFILES;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses
ShellApi;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
// Disable drag accept files
DragAcceptFiles(Self.Handle, true);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
// Enable drag accept files
DragAcceptFiles(Self.Handle, true);
end;
procedure TForm1.WMDropFiles(var Msg: TWMDropFiles);
begin
// Show a message
ShowMessage('File dropped');
// Set the message result
Msg.Result := 0;
inherited;
end;
end.
Like I said, when I drag and drop a file on the form, I can see that the file is accepted when dragged onto the form but when I drop the file, the WMDropFiles procedure is not called.
I also tried enabling the DragAcceptFiles in the CreateWnd procedure. But it still does not work.
...
public
procedure CreateWnd; override;
procedure DestroyWnd; override;
...
procedure TForm1.CreateWnd;
begin
inherited;
DragAcceptFiles(Handle, True);
end;
procedure TForm1.DestroyWnd;
begin
DragAcceptFiles(Handle, False);
inherited;
end;
I even tried running the Delpi IDE as Administrator.
Could it be a limitation of the Community Edition or am I missing something?
Addendum
I've now added a button to send a message WM_DROPFILES.
procedure TForm1.Button1Click(Sender: TObject);
begin
SendMessage(Self.Handle, WM_DROPFILES, Integer(self), 0);
end;
When I click the button, the WMDropFiles procedure is called. So then it works.
Ok, very interesting. I found this article:
How to Enable Drag and Drop for an Elevated MFC Application on Vista/Windows 7
So I added the following to my form create procedure:
procedure TForm1.FormCreate(Sender: TObject);
begin
// Enable drag accept files
DragAcceptFiles(Form1.Handle, true);
ChangeWindowMessageFilter (WM_DROPFILES, MSGFLT_ADD);
ChangeWindowMessageFilter (WM_COPYDATA, MSGFLT_ADD);
ChangeWindowMessageFilter ($0049, MSGFLT_ADD);
end;
And now it is working!
I changed the topic to include text "TListView" because I actually want to drop files on a TListView. Since I've solved the problem with dropping files on a form, I still had an issue with dropping files on a TListView.
So to drop the files on a TListView, you have to change the Handle to the listview's handle:
// Enable drag accept files
DragAcceptFiles(MyListview.Handle, true);
and
// Disable drag accept files
DragAcceptFiles(MyListview.Handle, false);
But that alone is not enough. You then need a Application events handler to catch the messages and handle them accordingly. So I just added a TApplicationEvents component to the form and added the following to the OnMessage event:
procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG;
var Handled: Boolean);
begin
// If it is a drop files message and it is for the listview
if ((Msg.message = WM_DROPFILES)and (Msg.hwnd = MyListView.Handle)) then
begin
// Handle your dropped data here
end;
end;

Can't Free caller component

I'm trying to free a component when i click it. So, i've written the simplest code i could imagine to achieve this: a procedure that frees it's sender. But on Delphi 7 (Tried on Delphi XE 10 and it worked with no errors) it sometimes throws an Access Violation or Abstract Error randomly. The easiest way to replicate this is to insert like 30 Buttons and assign an onclick procedure with the code below, then click them.
I've tried the two codes below, both on onclick:
procedure FreeMe(Sender: TObject);
begin
TButton(Sender).Free;
end;
or
procedure FreeMe(Sender: TObject);
begin
(Sender as TButton).Free;
end;
You need to delay the freeing until after the button's OnClick event handier has fully exited. It is important that the freeing happens when the object being freed is idle and not in the middle of processing anything.
One way to do that is to use PostMessage(), eg:
var
MyReleaseWnd: HWND;
procedure TMyMainForm.FormCreate(Sender: TObject);
begin
MyReleaseWnd := AllocateHWnd(MyReleaseWndProc);
end;
procedure TMyMainForm.FormDestroy(Sender: TObject);
begin
DeallocateHWnd(MyReleaseWnd);
end;
procedure TMyMainForm.MyReleaseWndProc(var Message: TMessage);
begin
if Message.Msg = CM_RELEASE then
TObject(Msg.LParam).Free
else
Message.Result := DefWindowProc(MyReleaseWnd, Message.Msg, Message.WParam, Message.LParam);
end;
procedure DelayFreeMe(Sender: TObject);
begin
PostMessage(MyReleaseWnd, CM_RELEASE, 0, LPARAM(Sender));
end;
Alternatively, in 10.2 Tokyo and later, you can use TThread.ForceQueue() instead:
procedure DelayFreeMe(Sender: TObject);
begin
TThread.ForceQueue(nil, Sender.Free);
end;
Either way, you can then do this:
procedure TSomeForm.ButtonClick(Sender: TObject);
begin
DelayFreeMe(Sender);
end;

ShowMessage when open Form (Designer Time)

I want show a notify (eg. ShowMessage) when a programmer open a Form in designer time.
Is possible? How?
Thanks.
PS: Delphi XE7 / VCL
I have a project with more than 700 form's, but when the programmer opens a specific one, I want to be given a notification (eg ShowMessage) stating that there are, for example, comments at the beginning of the .pas file.
This should happen in any form.
If you want to do this for ANY form, there is a straightforward way to do it. (As per David Heffernan's comment, whether your user will thank you it another matter, but anyway ...)
It involves installing a package in the IDE which installs an object which implements the IDesignNotification interface.
To use, create a new form and add a TMemo to it, rename the form to DesignNotifierForm, save it to disk then copy the code below into it. Then create a new package and add the unit to it. Then compile and install the package. In older Delphi versions like D7, there is an install button in the Package Editor, whereas in more recent versions like D10 Seattle, you go to View | Project manager in the IDE, then right-click on the BPL file in the pop-up and select Install from the pop-up context menu.
As you can see, in addition to the form, the unit declares a notifier object, TDesignNotification which implements an interface so that it can be registered with the IDE designer and receive notifications from it. The only one which is of interest from your pov is DesignerOpened, which iswhere you can call ShowMessage or do whatever you want.
The TDesignNotifierForm is included mainly as a simple way to experiment with & observe the notifications that the TDesignNotification receives, The TDesignNotification would work perfectly well without the form, though.
Btw, you might want to take a look at the ToolsAPI.Pas unit, which contains a host of interfaces which can be used to interact with the IDE.
unit DesignNotifierFormu;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, StdCtrls, TypInfo, ToolsApi, DesignIntf;
type
TDesignNotifierForm = class(TForm)
Memo1: TMemo;
Panel1: TPanel;
private
public
procedure Log(const Title, Msg : String);
end;
TDesignNotification = class(TInterfacedObject, IDesignNotification)
F : TDesignNotifierForm;
procedure ItemDeleted(const ADesigner: IDesigner; AItem: TPersistent);
procedure ItemInserted(const ADesigner: IDesigner; AItem: TPersistent);
procedure ItemsModified(const ADesigner: IDesigner);
procedure SelectionChanged(const ADesigner: IDesigner;
const ASelection: IDesignerSelections);
procedure DesignerOpened(const ADesigner: IDesigner; AResurrecting: Boolean);
procedure DesignerClosed(const ADesigner: IDesigner; AGoingDormant: Boolean);
constructor Create;
destructor Destroy; override;
end;
var
DesignNotification : TDesignNotification;
implementation
{$R *.dfm}
procedure SetUp;
begin
DesignNotification := TDesignNotification.Create;
RegisterDesignNotification(DesignNotification);
end;
procedure TDesignNotifierForm.Log(const Title, Msg: String);
begin
Memo1.Lines.Add(Title + ': ' + Msg);
end;
constructor TDesignNotification.Create;
begin
inherited Create;
F := TDesignNotifierForm.Create(Nil);
F.Show;
F.Log('Event', 'Notifier created');
end;
procedure TDesignNotification.DesignerClosed(const ADesigner: IDesigner;
AGoingDormant: Boolean);
begin
end;
procedure TDesignNotification.DesignerOpened(const ADesigner: IDesigner;
AResurrecting: Boolean);
var
C : TComponent;
Msg : String;
begin
C := ADesigner.Root;
if C <> Nil then begin
Msg := C.ClassName;
// At this point, you can call ShowMessage or whatever you like
ShowMessage(Msg);
end
else
Msg := 'no root';
F.Log('Designed Opened', Msg);
end;
destructor TDesignNotification.Destroy;
begin
F.Close;
F.Free;
inherited;
end;
procedure TDesignNotification.ItemDeleted(const ADesigner: IDesigner;
AItem: TPersistent);
begin
end;
procedure TDesignNotification.ItemInserted(const ADesigner: IDesigner;
AItem: TPersistent);
begin
end;
procedure TDesignNotification.ItemsModified(const ADesigner: IDesigner);
begin
end;
procedure TDesignNotification.SelectionChanged(const ADesigner: IDesigner;
const ASelection: IDesignerSelections);
begin
end;
initialization
SetUp;
finalization
if DesignNotification <> Nil then begin
UnRegisterDesignNotification(DesignNotification);
// Evidently the following is superfluous and results in a double-free DesignNotification.Free;
end;
end.
I want to be given a notification (eg ShowMessage) stating that there are, for example, comments at the beginning of the .pas file.
Well, the code above shows you how to provide some kind of event when a form is opened. How to do something like extract comments at the beginning of the file is a different technical problem really, and should be raised in a new question if you get stuck trying to do it.
Btw, one of the comments on your q pointed you in the direction of a code snippet by Dr Bob. That's fine as far as showing the technique is concerned, but would only do what you want if you were to install your own form in a package.

Unintended tStringGrid.OnFixedCellClick firing behind tOpenDialog

I use Delphi Berlin on Windows 10. I need to use tOpenDialog on a tStringGrid based tForm.
When I double click a file which overlaps a fixed column or row on an open dialog onFixedCellClick event fires automatically right after the disapperance of the open dialog. In the following image the file is on the same position of fixed row which is the first line.
type
TForm1 = class(TForm)
StringGrid1: TStringGrid;
OpenDialog1: TOpenDialog;
procedure FormClick(Sender: TObject);
procedure StringGrid1FixedCellClick(Sender: TObject; ACol, ARow: Integer);
procedure FormCreate(Sender: TObject);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
StringGrid1.Options := StringGrid1.Options + [goFixedColClick, goFixedRowClick];
end;
procedure TForm1.FormClick(Sender: TObject);
begin
OpenDialog1.Execute;
end;
procedure TForm1.StringGrid1FixedCellClick(Sender: TObject; ACol, ARow: Integer);
begin
Caption := '';
end;
In most cases I can handle this by moving the dialog window or clicking the file once and clicking open button but I can't guarantee that other people who will use this would do that.
What is the reason and how can I solve this problem?
I believe this is a problem in how TCustomGrid triggers its OnFixedCellClick event on a mouse-up message (in its overriden MouseUp method) without checking if there was a corresponding mouse-down message (FHotTrackCell.Pressed). A quick fix (if you can copy and modify Vcl.Grids): on line 4564 in Berlin (in TCustomGrid.MouseUp method add another condition to check, leading to the call to FixedCellClick):
if ... and FHotTrackCell.Pressed then
FixedCellClick(Cell.X, Cell.Y);
In other words, don't call FixedCellClick if a mouse-up comes without a prior corresponding mouse-down.

Delphi: PopupMenu doesn't work in my component

English Translation (been a while, so may not be entirely accurate; used google translate for the parts I had trouble with):
I'm working on a Visual Component in Delphi (it's not a standard Delphi component) which possesses a property called PopupMenu. I associated the property PopupMenu in the component with the PopupMenu, but when I click the right button [of the mouse], I see nothing.
I also tried to force it to display with this code:
x:= Mouse.CursorPos.X;
y:= Mouse.CursorPos.Y;
// //showmessage(inttostr(x)) PopupMenu1.Popup(x,y);
I have two questions:
How do you know that the right click of the mouse is active? Have any of you encountered this type of problem? Thank you for your answers.
Thanks
EDIT
Here is the procedure that I'm using to execute the PopupMenu1: procedure
TForm6.GeckoBrowser1DOMMouseDown(Sender: TObject; Key: Word);
var x,y:integer;
begin
if key=VK_RBUTTON then begin
x:= Mouse.CursorPos.X;
y:= Mouse.CursorPos.Y;
//showmessage(inttostr(x)) PopupMenu1.Popup(x,y);
end;
end;
This will never work. You cannot mix code in a form with the component code.
I would suggest something like this:
interface
type
TGeckoBrowser = class(....
private
FPopupmenu: TPopupMenu;
protected
...
procedure MouseUp(Sender: TObject; Key: Word); override;
...
published
property PopupMenu: TPopupMenu read FPopupMenu write FPopupMenu;
end;
implementation
....
procedure TGeckoBrowser.MouseUp(Sender: TObject; Key: Word);
var
x,y: integer;
begin
inherited;
if (key=VK_RBUTTON) and Assigned(PopupMenu) then begin
x:= Mouse.CursorPos.X;
y:= Mouse.CursorPos.Y;
PopupMenu.Popup(x,y);
end; {if}
end;
or if you do not want the OnMouseUp to fire when a popup menu appears do:
implementation
....
procedure TGeckoBrowser.MouseUp(Sender: TObject; Key: Word);
var
x,y: integer;
begin
if (key=VK_RBUTTON) and Assigned(PopupMenu) then begin
x:= Mouse.CursorPos.X;
y:= Mouse.CursorPos.Y;
PopupMenu.Popup(x,y);
end {if}
else inherited;
end;
See the difference? Popupmenu is now a part (well linked part anyway) of your component and not something that just happens to be on the same form.

Resources