Delphi Modal Form position - delphi

I have a form that is being opened by another form.
I set the Position to be poOwnerFormCenter, so that the new form is opened where the original was
However, when I move this new form and then go back to the original, its shown where it was when I first opened the new form, not where I closed it
How would I fix this?
Thanks!

I'm a bit confused by your question so I'll clarify what I'm try to solve here!...
I think what you're trying to do is
When Form2 Opens, it is positioned centrally to Form1 and Form1 is hidden.
When Form2 Closes, Form1 is shown (exactly where it was hidden).
I think you want to do is have Form1 Show where Form2 was closed.
So I'm guessing that you have some code like...
procedure TForm1.ButtonClick(Sender: TObject);
begin
Form2.ShowModal;
end;
and you were expecting Form2 to update form1's position because you set Form2's position to poOwnerFormCenter
Well if I guessed all that correctly then all you need to do to update Form1's position when Form2 closes is
procedure TForm1.ButtonClick(Sender: TObject);
begin
Form2.ShowModal;
Left := Form2.Left;
Top := Form2.Top;
end;

The problem is that you are reusing the same instance of the modal form. Setting the position only works the first time you show the form. You have to options here:
Option 1
You can destroy the modal form every time it closes. One of the ways of doing that is having this line on the OnClose event of the form:
Action = caFree;
Of course, that means you have to recreate the modal form from the caller every time as well.
Option 2
You have to manually set the modal form's position on the OnShow event.
Use the option which best suits you.

This is (I guess) because you recreate the form every time you display it. That is, you do
with TForm2.Create(nil) do
try
ShowModal;
finally
Free;
end;
Because you create a new instance of the TForm2 class every time you show it, and destroy it when the form has closed, the position changes; indeed, the new TForm2 object cannot possibly remember the position of any previous TForm2 object. They are two different objects (yes, same class, but that doesn't matter)!
The simplest solution is to add the TForm2 to the 'auto-create forms' list in the Project Options. It is there by default, but if you create it manually (as I think you do, and as in the code snippet above), you should have removed it from the list of forms that are automatically created...
Then you make sure that Unit1 uses Unit2, so that you can access the global Form2 variable in Unit2 from Form1 that resides in Unit1. While editing Unit1, press Alt+F11 to do this.
Then you can just show Form2 by doing
Form2.ShowModal;
The first time it is shown, it will respect its Position parameter, and position itself above its owner form. But then it will remember its position, so the second time you display it, it will be right where it closed the first time.

Related

Setting the WindowState is showing the form

Many of my application forms inherit from a base form which loads the saved window size, position and state recorded during FormClose and applies it during FormShow.
The inherited; line in the descendant version of FormShow is in the usual place at the start of the method, and is generally followed by a fair amount of code that requires the visual components on the form to have been created so they can be manipulated and set up.
The problem I am having is that the form is usually hidden until the end of the descendent FormShow event, which is the expected behaviour, unless the WindowState is being set to wsMaximized in the ancestor class FormShow event. In that case, the form becomes visible as soon as the inherited; line is executed, and you get to watch as the remaining visual elements get organised.
When setting the WindowState property of a VCL.Forms.TForm, the following code is executed:
procedure TCustomForm.SetWindowState(Value: TWindowState);
const
ShowCommands: array[TWindowState] of Integer =
(SW_SHOWNORMAL, SW_MINIMIZE, SW_SHOWMAXIMIZED);
begin
if FWindowState <> Value then
begin
FWindowState := Value;
if not (csDesigning in ComponentState) then
begin
if Showing then
ShowWindow(Handle, ShowCommands[Value])
else if HandleAllocated and (FWindowState = wsMaximized) then
RecreateWnd;
end;
end;
end;
The apparent cause of the issue is somewhere in that method; either the ShowWindow or the RecreateWnd, which seems to be triggering the form to be immediately painted.
Apart from moving my LoadFormState(Self); method from TBaseForm.FormShow to TBaseForm.FormActivate, is there any other way to set the form to be maximized without it actually showing the form?
You should not be calling your LoadFromState procedure either from TBaseForm.FormShow or TBaseForm.FormActivate.
TBaseForm.FormShow is called every time visibility of your form changes from False to True.
So for instance if you hide your from when showing another and then show it again after another form is closed TBaseForm.FormShow will fire and thus you will load form's dimensions and position from the saved state which might not be the same state as of when the form was hidden. User might have moved and resized the from since application was started.
This will lead to form moving its position without users wish to do so and thus it will annoy your users as hell.
Also don't use TBaseForm.FormActivate as this fires every time Form receives focus. So changing focus from another back to this one will the TBaseForm.FormActivate and thus change your form dimensions and position to the saved state which might not be teh state when form lost its focus.
The correct place for you to call your LoadFormState procedure with which you load initial properties of your form is within the forms constructor.

Reassign an OnCellClick event

I have an SQLite database with many Tables and one is named "tblAccounts"
I have a dlgCommon that has a TDBGrid on it with the dbgridAccounts.DataSource:=srcAccounts
I have several other Dialogs all of which at some time need to click a button and show the Accounts Grid to select an Account from. Rather than have many Forms all with their own TDBgrid.DataSource:=srcAccounts I am doing this...
procedure TdlgFolders.btnAcctSelClick(Sender: TObject);
begin
dlgCommon.pnlAccounts.Parent:=Self;
dlgCommon.pnlAccounts.Left:=dbedAccount.Left;
dlgCommon.pnlAccounts.Top:=dbedAccount.Top+dbedAccount.Height+2;
dlgCommon.pnlAccounts.Width:=190;
end;
When the user has the dlgFolders active and clicks "btnAcctSel" it all does as I need and shows the Grid. But, when the user clicks the Grid-Cell I am at a loss where/how to put the dbgridAccountsCellClick(Column: TColumn); Handler.
I tried putting it in the dlgCommon and it compiles, but is not used as that is no longer the Parent when the Grid is visible and Cell-clicked in one of the other Dialogs.
I would prefer to keep using the single-Grid approach as the user gets to set the column widths, Row-colors etc and I'd rather not make them do that in every Form where the Accounts Grid is needed.
How can I reassign the dlgCommon.AccountsCellClick so that the click is trapped and used in dlgFolders and other Dialogs that call it too?
I'm not sure I folllow your structure and design, but I would place the grid that shows the accounts on a TFrame. This TFrame would hold all event handlers you need for the grid, in addition to the grid itself.
Then, whenever you need to show the grid, you instantiate the frame, assign its parent and the grid and event handlers are ready for use.
On a second and third reading, if dlgCommon is a form with a hierarchial structure like
dlgCommon: TdlgCommon
pnlAccounts: TPanel
AccountsGrid: TDBGrid
it appears that you have tried to "rip-out" (by changing the parent) the pnlAccounts from that form and then the event handlers don't work, just as you have noticed.
The idea of changing a components parent like this is a really bad idea, because when you assign a new parent to the grid, it will ofcourse not anymore show up in dlgCommon. It can be visible in only one dialog at a time.
If you want the grid simultaneously visible on various forms for (at least) some period of time, I would still use a TFrame as I already suggested.
In this case you can add an OnCellClick event manually to the forms private section
procedure DBGridCellClick(Column: TColumn);
and implement it in the form
procedure TForm1.DBGridCellClick(Column: TColumn);
begin
// whatever you want to do
end;
And you instantiate the frame as follows:
procedure TForm1.Button2Click(Sender: TObject);
begin
frame:= TFrame3.Create(self);
frame.Parent := self;
frame.Left := 8;
frame.Top := 75;
frame.DBGrid1.OnCellClick := DBGridCellClick;
end;
If, on the other hand, the user needs to see the grid only briefly, to select an account (and be done with it), I would simply show the dlgCommon modally.

Appearing With Effect, When Showing Window A Second Time

I need to know the reason (and possible workarounds) for a strange behavior in Windows 7.
I have Form1 with a button on it and also a second form called Form2. In button's click handler I've:
Form2.Show;
After running my program, the first time I click on the button, my Form2 appears with a nice fade-in effect (sorry, I don't know the exact effect name in Windows 7. Tell me if you know!). OK. I close Form2 and click the button again. This time Form2 appears with no effect at all.
I want my Form2 to appear with that effect every time I click on the button. To be more specific, I need the main form of a real application to appear when user clicks on a tray icon. The first time clicking on the tray icon shows the window with animation, but second time it doesn't. How can I solve that?
Windows shows that animation the first time a window is shown. So all you need to do is make sure that every time your form is shown, the associated window is being shown for the first time.
You could destroy the form when it closes and create a new instance when you need to show it again. However, that may be inconvenient for you depending on how your form manages state. Judging from your edit and comments, you cannot afford to destroy the form when you close it. Instead you would need to force a new window to be created for your form, each time you show it.
For example, add a call to DestroyHandle in the OnClose event of the form. Or make a call to RecreateWnd immediately before you show the window. Note that the latter will involve making the protected method RecreateWnd visible to whoever calls Show on the form.
Remove your Form2 from the auto-create list. (Project->Options->Forms, click Form2 in the left pane, and click the button labeled > to move it to the right side.)
Change your ButtonClick handler:
procedure TForm1.Button1Click(Sender: TObject);
var
TempFrm: TForm2;
begin
TempFrm := TForm2.Create(nil);
TempForm.Show;
end;
Add a FormClose event to TForm2 (while you're at it, delete the global Form2: TForm2; variable just above the implementation clause, so you don't use it again by mistake)::
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
end;
As a note: You should go to Tools->Options->VCL Designer, and uncheck Auto create forms and data modules in the Module creation options at the bottom. Auto-creating forms is almost always a bad idea; the only thing I ever allow to be auto-create (other than the main form) is any TDataModule that needs to be available immediately, and I move it to the top of the auto-create list you saw in the first paragraph so that it's created before the main form.

Delphi 2010 forms shows on "wrong" monitor

I have a simple test app, with one empty form, and a second containing a TButton. The button script is like this:-
procedure TForm1.Button1Click(Sender: TObject);
begin
Form2.Show();
end;
Form2 is auto-created at startup. There's no other code at all.
When I run the app, I can press the button and Form2 appears. I can reposition/resize Form2 on the primary monitor, and then close it. If I press the Form1 button again, Form2 correctly reappears in the position it was last at. All fine so far...
However, if I position Form2 onto a secondary monitor, close it, and then press the button, then Form2 appears back on the primary monitor!
I want the form to reappear on the monitor it was last on - how can I get this behaviour??
This is because the default value for the form's DefaultMonitor property is dmActiveForm.
Set Form2's DefaultMonitor to dmDesktop, and the problem is resolved.

Why doesn't OnUpdate trigger for invisible components [duplicate]

This question already has answers here:
How can I use an action to determine a control's visibility?
(3 answers)
Closed 8 years ago.
When I make a component invisible by setting the connected TAction to invisible, the onupdate event will not trigger anymore. To recreate, do the following.
Create a new VCL forms application
Drop a button, a checkbox and an actionlist on the form.
Create a new action, and connect the button to it.
Write the following code for the actions OnExecute and OnUpdate event:
procedure TForm1.Action1Execute(Sender: TObject);
begin
ShowMessage('Test');
end;
procedure TForm1.Action1Update(Sender: TObject);
begin
TAction(Sender).Enabled := not CheckBox1.Checked;
TAction(Sender).Visible := TAction(Sender).Enabled;
end;
Run the application. The button is visible, and works properly. Check the checkbox, and the button disappears. Uncheck the checkbox. The button doesn't appear. In fact, if you put a breakpoint in Action1Update, you'll never get to it. Why is this, and how do I fix it?
No need to fix this, it works as designed. Only visible controls need to update their state, so only actions whose linked controls are visible are updated. When you hide the button there's no more reason to update the action.
Have the OnUpdate only call a separate routine that does what is required. Then you can call that routine from other places. Action lists were designed for that.
I understand what you're trying to do, and it makes sense that you would want it to work that way. However, here's a workaround for the way it does work.
You can update other controls in an OnUpdate also. You're not limited to updating the control that receives the notification. So, in the action for the control that determines visibility, you can set the visibility of the other controls there. In your case, that's the checkbox:
Create a new action (Action2) and assign it to Checkbox1.
Then in the checkbox action's OnUpdate:
procedure TForm1.Action2Update(Sender: TObject);
begin
Button1.Visible := TAction(Sender).Checked;
end;
Be sure to assign an OnExecute to the checkbox as well. Something as simple as this is fine:
procedure TForm1.Action2Execute(Sender: TObject);
begin
TAction(Sender).Checked := not TAction(Sender).Checked;
end;
To me, this still makes logical sense. You'll be able to look in one spot to see all of the controls whose visibility relies on that checkbox being set.
You can override the InitiateAction method on the form. This will happen whenever the application goes idle, just as on OnUpdate event does for each action.

Resources