I have two delphi forms; frmHome and frmStats. There is a button on the main form (frmHome) and when you click on it it takes you to frmStats.
On btn click in frmHome:
frmStats.ShowModal;
Hide;
This works fine but on frmStats there is also a button that I would like to take the user back to the main form when clicked on. The problem is I get error messages.
If I make reference to the form like this I get the error message "cannot make visable window modal"
implementation
uses frmHome_u;
However if I make reference like this, then i get the message "circular unit reference"
interface
uses {a bunch of uses} , frmHome_u;
On btn click in frmStats:
frmHome.ShowModal;
Hide;
How do I do this?
Modal forms have owners. These owning forms are disabled when the modal form is shown. A form is always shown above its owner.
You are trying to make form1 be the owner of form2 and then in turn have form2 own form1. That circular ownership is not allowed. It would imply that both windows would be disabled, and each window on top of the other.
What you need to do is to close the modal form. Replace
frmHome.ShowModal;
Hide;
with
ModalResult := mrOK;
If you want both forms to be visible and enabled at the same time, then you must not show them modally.
If I understand what you are trying to do then this should work just fine.
frmStats.Enabled := True;
frmStats.Show;
Enabled := False;
Hide;
And you have the reciprocal code in the other form.
Because the other form is disabled, it can not be brought into focus until you enable it.
EDIT
If the users closes FrmStats and FrmHome is the mainform then you must enable it in the onclose event.
Related
In the project source, I detect a previous instance of my app, and open it then terminate the new instance.
Remember, I do this in the project source.
The application has a main form that can open a second modal form. If the second, modal, form is not open when the app is minimized (or the user switches to another application) it works fine.
The old main form is opened and the new instance is terminated. However, if the second modal form is minimized, its parent main form is also minimized. That's good UI behavior.
The problem exists when attempting to restore the modal form by a new instance. I can't seem to get the modal form to restore.
I can restore the main form, however the modal form still has focus, but is hidden.
This prevents any user action on the now visible main form. From the user's point of view, the app is hung.
The modal form's caption does not change, so it's easy to get its handle.
The app has a DataModule, which is created first. I do all the duplicate app testing in the project source after the DM is created, but before any other forms are, and well before Application.Run.
That assures that the handles I acquire come from the existing app.
I've tried:
PreviousHandle := FindWindow(nil, PAnsiChar('Form Editor'))
ShowWindow(PreviousHandle,SW_RESTORE);
ShowWindow(PreviousHandle,SW_NORMAL);
BringWindowToTop(PreviousHandle);
The best I've gotten out of variations of this is restoring the main form.
When the handle is for the modal form, it sometimes shows in the lower left as a minimized form.
I say sometimes because I've tried so many variations of the above that I don't remember exactly how this code sample behaved.
All I know is, whatever I've tried from the project source did not restore the modal form, so I tried sending a custom Windows Message to the modal form.
I know the message arrived and the message handler caught it. Example:
if not PostMessage(PreviousHandle,WM_RESTORE_FORM,PreviousHandle,0) then
ShowMessage('Can''t automatically show the Form program. '+
'Please click the taskbar icon.');
Here's what the message handler looks like. It is in the Modal Form. As you can see, I pass the form's handle as a Message param. I've verified that it arrives.
Here's the code in a TApplicationEvents OnMessage event:
procedure TfrmDisplayForm.ApplicationEvents1Message(var Msg: tagMSG;
var Handled: Boolean);
var
oldHwnd : THandle;
begin
if Msg.message = WM_RESTORE_ACORD then
begin
oldHwnd := Msg.lParam;
if ForceForegroundWindow(OldHwnd) then ShowMessage('true') else ShowMessage('False');
Handled := True;
end;
end;
Here's the function that is called. However, I've tried various ShowWindows(...) combinations with no luck. Actually, this function didn't do the trick either.
function TfrmDisplayForm.ForceForegroundWindow(hWnd: THandle): BOOL;
var
hCurWnd: THandle;
begin
{credit Peter Below}
hCurWnd := GetForegroundWindow;
AttachThreadInput(GetWindowThreadProcessId(hCurWnd, nil),
GetCurrentThreadId, True);
Result := SetForegroundWindow(hWnd);
AttachThreadInput(GetWindowThreadProcessId(hCurWnd, nil),
GetCurrentThreadId, False);
end;
I've been working on this for a couple of days, and don't know what I'm doing wrong.
Question: How can I restore the modal form? It must be possible because it restores perfectly when the taskbar icon is clicked.
Don't try to restore individual forms instead use TApplication.Restore
Calling TApplication.Restore will restore all of the application windows to the state before the whole application was minimized same as clicking on TaskBar icon of the application.
PS: I also hope you are not allowing users to minimize individual Modal forms. Or you may also run into a problem where application seems to be stuck.
I have an application with a login form (which is the main form) and a main form, which is opened after the log-in one is filled in with the correct info.
However, the program icon does not appear on the taskbar. I would like to ask, why does this happen?
I don't think there is anything bizarre in this behaviour.
I assume You hide login form after user provides login information?
By default Main form owns taskbar:
Application->MainFormOnTaskBar = true;
So either make frmMain "main" instead of LoginForm and create login form dynamically before main Form (this will make login form invisible on taskbar) or add following code to frmMain -
in header:
void __fastcall CreateParams(Controls::TCreateParams &Params);
and in the .cpp file:
void __fastcall frmMain::CreateParams(Controls::TCreateParams &Params)
{
TForm::CreateParams(Params);
Params.ExStyle = Params.ExStyle | WS_EX_APPWINDOW;
Params.WndParent = ParentWindow;
}
WS_EX_APPWINDOW "Forces a top-level window onto the taskbar when the window is visible."
As described in this MSDN article
and Embarcadero DocWiki.
Is there some method of detecting if there is a modal form showing in my VB6 application.
I want to show another form modally if there is a modal form showing and modeless if there isn't a modal form showing.
I know I can do some error handling to detect this and show modally if the error occurs:
Public Sub ShowFormModeless(frm As Form, Optional ownerForm As Form = Nothing)
On Error GoTo ShowModal
Call frm.Show(, ownerForm)
Exit Sub
ShowModal:
Call frm.Show(vbModal, ownerForm)
End Sub
But I want to be able to detect this without the error being thrown.
Try checking App.NonModalAllowed. Note that this property does not detect modal forms being shown from other (than current module) projects, i.e. if your application consists of main executable and several ActiveX dlls each of these projects will have a separate App object and Forms collection.
The result is that if a dll is showing a modal form only its own project's App.NonModalAllowed is flagged. So once again, App.NonModalAllowed is not a process-wide flag but a per project one.
Note that this flag tracks VB6 only forms, so if you are using API dialogs (open/save file, color picker, etc) this does not enter VB6 runtime's modal loop, so nothing is flagged.
If you application is a single exe then using this flag works as expected.
Is there a property that tells if a form is deactivated by other form ShowModal procedure ?
EDIT :
My program has a tray icon that brings to front the main form when it's clicked. I want to disable this when another window is shown in modal state. Because not doing so the main form (which is disable) will cover the modal form and completly block my program.
This behaviour is to be expected. When a modal form is shown, the other forms are disabled. You don't need to disable anything at all, the framework already handles it all for you. The beep is sounding because you are attempting to interact with a disabled form.
If you want to be notified when your window has been disabled, for any reason, not just because a modal form has been shown, listen to the WM_ENABLE message. To test whether or not your main form has been disabled. Do that by calling the IsWindowEnabled Win32 function.
Having said that I feel that it is likely you've not diagnosed the issue correctly. It sounds like you might be suffering from window ownership problems, which are common in Delphi 6. Or perhaps you are attempting to restore the application incorrectly from your notification icon code. Use Application.BringToFront for that.
The VCL's handling of modal dialogs seem very mixed up. When you show a system provided modal dialog, e.g. MessageBox, windows are disabled whether or not they are visible. However, the VCL only disables visible windows when ShowModal is called. What's more, you cannot use Enabled to test whether or not the window is disabled, you must use the IsWindowEnabled Win32 function.
You can test Application.ModalLevel at any point in time to find out if there's a modal form. E.g.:
if Application.ModalLevel = 0 then
MainForm.Visible := True;
Note that non-TCustomForm descendants will not set modal level, API dialogs like a file open dialog or MessageBox for instance. If there's a possibility of such a thing, you might surround code that runs those dialogs with ModalStarted and ModalFinished.
It doesn't seem necessary in your case, but if you somehow need to be notified that a form/dialog is going modal, you can attach a handler to Application.OnModalBegin and Application.OnModalEnd events. You can use an TApplicationEvents component for that.
I want to push toolstripbutton down in my code and I can't seem to be able to do that. I know on Delphi RAD Studio or XE, you can do the following and cause the button to be pressed.
ToolStripButton1.Down := true;
The only ToolStripButton property I see that comes close to "down" is checked true or false. If I do set it to true, it only highlights the toolstripbutton not press it down.
Here is how the button looks when I put my mouse on it and click:
You can clearly see that the Zoom In button is down.
Here is how the button looks when I try to do the samething through my code by setting CheckOnClick true and Checked true.
In this image, the only thing you can see is the blue box around it. I suppose if I had used just the text on the button, you will see that the whole button filled with blue color to show that it was pressed.
I also have toolstrip button in my other program which acts the same way but I had to use imagelist control to switch between pressed or down or checked verses not pressed or down or checked.
So, is there a way to press the ToolStripButton programmatically in Delphi Prism or C#?
Set the ToolStripButton.CheckOnClick property to True. (It's found in the Behavior section of the Items Collection Editor.)
This makes clicking it just like toggling the Down property in a Delphi TSpeedButton (making it flat or depressed), and if ToolStripButton1.Checked is the equivalent of if SpeedButton1.Down in Delphi.
To set up the test, I did the following:
Created a new Winforms application
Dropped a ToolStrip onto the new MainForm
Added four ToolStripButton items and gave them images to make them easier to see.
Set the CheckOnClick property to True for each of them
Set the Checked property of toolStripButton1 to True;
Added the code below to toolStripButton1.Click
method MainForm.toolStripButton1_Click(sender: System.Object; e: System.EventArgs);
begin
toolStripButton2.Checked := not toolStripButton2.Checked;
toolStripButton4.Checked := toolStripButton2.Checked;
end;
Running the app (initial startup, toolStripButton1 checked and the others unchecked):
The first button is clearly down, and the rest are up.
After clicking toolStripButton1 once:
The first button is now up (unchecked) and the second and fourth are down (checked). (I should pay more attention to the consistency in sizing if I do successive images in future posts.)
If have placed this code in the preceding control in the 'Leave' event.
Private Sub PurposeComboBox_Leave(sender As Object, e As EventArgs) Handles PurposeComboBox.Leave
Me.AppliancesForSelectedFunctionToolStripButton.PerformClick()
End Sub
I do not know if you could place code in Form Load or not.
Hope this helps.