Why is it that sometimes showing hiding forms misfires? - delphi

My form1 is the mainform. I use it for login purposes and after I dont need it
I hide it. If the login is successful, on button click,form3 is called.
procedure TForm1.AdvGlowButton1Click(Sender: TObject);
begin
ABSQuery4.Active:=false;
ABSQuery4.SQL.Clear;
ABSQuery4.SQL.Add('select .....bla,bla,bla....');
ABSQuery4.Open;
if ABSQuery4.FieldByName('passsword').AsString<>''
then begin
Form3.Show;
Form1.Hide;
end else begin
cxTextedit1.Text := '';
showmessage('wrong password');
end;
end;
Now I am noticing that sometimes the event produces strange results.
Form3 is shown but Form1 remains visible also. For showing the main form
from form3 I use :
procedure TForm3.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Form1.Show;
end
Since I need the application icon for Form3, I have there :
procedure TForm3.CreateParams(var Params: TCreateParams) ;
begin
inherited;
Params.ExStyle := Params.ExStyle or WS_EX_APPWINDOW;
Params.WndParent := 0;
end;
Can you tell me am I messing anything up or is there a better way to
make the forms behave properly?

There is a better way to do this. For a start, you should give your forms meaningful names!
Your real problem is that your main form is being hidden because you are using it as your login form. You make life needlessly difficult and complicated by doing that.
So the main piece of advice is that you should make you real main form be the Delphi main form. The Delphi main form is the first form created using Application.CreateForm. I suggest that you call Application.CreateForm exactly once, to create the main form.
This may leave you wondering how to create other forms. Well, you just create them using the standard constructor, just like any other object.
So your .dpr file code might look like this:
Application.Initialize;
LoginForm := TLoginForm.Create(nil);
try
if LoginForm.ShowModal <> mrOK then
exit;
finally
LoginForm.Free;
end;
Application.CreateForm(TMainForm, MainForm);
Application.Run;
Once you make this change you will find that you don't need to take steps to force the main form onto the taskbar.

Related

Application terminates for no apparent reason

I have setup my login procedure with an available form (Form4) and a MainForm.
On the available form (form4) I have:
var
Form4: TForm4;
procedure Login;
implementation
.....
The 'Login' procedure goes:
procedure Login;
begin
with TForm4.Create(nil) do
try
Application.MainForm.Hide;
if ShowModal = mrOK then
Application.MainForm.Show
else
Application.Terminate;
finally
Free;
end;
end;
Then on the same form I have a button to log in :
procedure TForm4.AdvGlowButton1Click(Sender: TObject); //the buton's property is ModalResult=mrOK
begin
DataModule2.LOGIN_QUERY.Active:=false;
DataModule2.LOGIN_QUERY.SQL.Clear;
DataModule2.LOGIN_QUERY.SQL.Add('select user,passsword from users where user='+QuotedStr(cxlookupcombobox1.text)+' and password='+QuotedStr(cxTextEdit1.Text));
DataModule2.LOGIN_QUERY.Open;
if DataModule2.LOGIN_QUERY.FieldByName('password').AsString<>''
then ModalResult := mrOK else
ModalResult := mrNone;
end;
The project source goes like this :
begin
Application.Initialize;
Application.MainFormOnTaskbar := False;
Application.CreateForm(TDataModule2, DataModule2);
Application.CreateForm(TMainForm, MainForm);
Application.CreateForm(TForm7, Form7);
Application.CreateForm(TARCHIVE, ARHCIVE);
Application.CreateForm(TForm10, Form10);
Application.CreateForm(TForm1, Form1);
Application.CreateForm(TForm6, Form6);
Application.CreateForm(TForm5, Form5);
Application.CreateForm(TForm9, Form9);
Application.CreateForm(TForm12, Form12);
Application.CreateForm(TForm12, Form12);
Application.CreateForm(TAboutBox, AboutBox);
Login;
Application.Run;
end.
Yet, every now and then when clicking the Login button on Form4 the application
terminates without no reason. Why is this happening ?
Should
Application.MainFormOnTaskbar := False;
be set to true perhaps?
Edit:
I edited the project file and the form4 on create event :
procedure TForm4.FormCreate(Sender: TObject);
begin
AdvGlowButton1.ModalResult := mrOK;
end;
and changed the project source :
{$R *.res}
var
MainForm: TMainForm;
begin
Application.Initialize;
Application.CreateForm(TMainForm, MainForm);
Application.MainFormOnTaskbar := False;
Application.CreateForm(TDataModule2, DataModule2);
Application.CreateForm(TForm7, Form7);
Application.CreateForm(TARCHIVE, ARCHIVE);
Application.CreateForm(TForm10, Form10);
Application.CreateForm(TForm1, Form1);
Application.CreateForm(TForm6, Form6);
Application.CreateForm(TForm5, Form5);
Application.CreateForm(TForm9, Form9);
Application.CreateForm(TForm12, Form12);
Application.CreateForm(TForm12, Form12);
Application.CreateForm(TAboutBox, AboutBox);
Login;
Application.Run;
end.
and I dont seem to be getting application closing....(it still does,ughh...)
edit2:
Tried this way. I set my AdvGlowButton1 to ModalResult=mrNone and the Form style to fsdialog:
procedure TForm4.AdvGlowButton1Click(Sender: TObject); //the buton's property is ModalResult=mrOK
begin
DataModule2.LOGIN_QUERY.Active:=false;
DataModule2.LOGIN_QUERY.SQL.Clear;
DataModule2.LOGIN_QUERY.SQL.Add('select user,passsword from users where user='+QuotedStr(cxlookupcombobox1.text)+' and password='+QuotedStr(cxTextEdit1.Text));
DataModule2.LOGIN_QUERY.Open;
if DataModule2.LOGIN_QUERY.FieldByName('password').AsString<>''
then ModalResult := mrOK else
dxStatusBar1.Panels[1].Text :='Wrong password !';
end;
this works most of the times and yet it sometimes closes when I start the application and hit the AdvGlowButton1 button (login button) . Another thing I figured out is missing, how do you prompt for the closure of the application on this login form as it expects only modal results?
Like #SertacAkyuz is hinting, one option would be that ShowModal does not return mrOK. Check the ModalResult value for this button and/or the event handler for the OnClick of this button to see if mrOK is the ModalResult that is returned in these cases...
If you click a button, then the OnClick event is fired and if the ModalResult of that button is set to anything, the Form's ModalResult will be set to that value. When an event (like an OnClick event) ends, the form checks its ModalResult value, and if it is set to anything other that 0 (zero), then the form closes and the value is returned as the result of the ShowModal function.
So from what information you have given, this seems like a likely scenario - the Form's ModalResult is set to some value that isn't mrOK. The form then closes, and your IF ShowModal test then terminates the application (since it didn't return mrOK).
Based on your code entering wrong password would also result in termination of your application becouse in that case the modal result returned would be mrNone and you only expecting mrOK to continue with your application.
So I recomend next changes:
First remove the modal result propery of your button. button modal result propery is mostly used only as a way to forward the information as of clicking on which specific button lead to closure of modal form.
Then change your buttons event code so that it only sets form modal result if the code is correct else it should show a message that the entered passowrd is incorect. Something like so:
procedure TForm4.AdvGlowButton1Click(Sender: TObject); //the buton's property is ModalResult=mrOK
begin
DataModule2.LOGIN_QUERY.Active:=false;
DataModule2.LOGIN_QUERY.SQL.Clear;
DataModule2.LOGIN_QUERY.SQL.Add('select user,passsword from users where user='+QuotedStr(cxlookupcombobox1.text)+' and password='+QuotedStr(cxTextEdit1.Text));
DataModule2.LOGIN_QUERY.Open;
if DataModule2.LOGIN_QUERY.FieldByName('password').AsString<>'' then
//Close the modal form with returning of mrOK as modal result
ModalResult := mrOK
else
begin
//No modal result should be set here or it would lead to closure of login form
MessageDlg('Entered password is incorect!',mtError, mbOKCancel, 0);
end;
end;
This is direct solution for your specific problem. But I would recomend you seriously reconsider your design of the whole login system. Why?
In you current design you create all the forms at the start of your application. Now while you do start with your main form hidden this still doesen't mean that your user can't access it.
Using a special software user could find a handle to your main form window and show it without going through your login process at all.
So the correct approach to avoid this would be to create the login form first and then only on sucsessfull login create the rest of your forms. But that would mean that your login form would actually become the main form of your application so you should take great care of not closing it as it would lead to the closure of your whole application.
You can check the example of how to implement such approach in my answer to another question here:
Delphi Change main form while application is running

Obtain data from available form to autocreated one

I would like to transfer data from the statusbar of Form2 (which is on the list of available forms and used for login purposes), to the statusbar on the MainForm (which is autocreated one).
If I use :
procedure TMainForm.FormShow(Sender: TObject);
begin
AdvOfficeStatusBar1.Panels[0].Text := Form2.AdvOfficeStatusBar1.Panels[0].Text;
end;
I get an access violation error. . Why is this happening? How can I get over this?
Based on your comment I would say that the following is occurring:
The code you would be using for your login form is this:
procedure Login;
begin
with TForm2.Create(nil) do
try
Application.MainForm.Hide;
if ShowModal = mrOK then
Application.MainForm.Show
else
Application.Terminate;
finally
Free;
end;
end;
What is happening here is that there is an implicit variable for TLoginForm. This is not the same as the auto created variable which would be Form2: TForm2; which sits in the TForm2 unit. This variable is freed directly after the form closes.
To see what I mean. If you delete the variable called Form2 from your application, the only part of the code that won't compile is your line in your original post.
What you would have to do if you wanted to do this sort of thing is something like this (I have changed the name of TLoginForm to your Form2).
procedure Login;
begin
Form2 := TForm2.Create(nil);
Application.MainForm.Hide;
if Form2.ShowModal = mrOK then
Application.MainForm.Show
else
begin
Form2.Free;
Application.Terminate;
end;
end;
The you would have to free your Form2 when you close your main form. I would really not recommend doing this sort of thing though. As a quick fix, you would be far better off saving the text AdvOfficeStatusBar1.Panels[0].Text in a global variable in the OnClose event of the Form2 with something like this:
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
g_MySavedVariable := AdvOfficeStatusBar1.Panels[0].Text;
end;
And then loading it in the main form with:
procedure TMainForm.FormShow(Sender: TObject);
begin
AdvOfficeStatusBar1.Panels[0].Text := g_MySavedVariable;
end;
Even this is not ideal but should get you up and running.
If you are looking for some code for a login form where you pass data between the login form and the main form then you are probably better off looking at this question in StackOverflow (delphi Login Form) along with the answers by David and Cosmin. My personal preference is for the solution by Cosmin as you don't need to mess with the DPR and there is no global variables involved.

How to keep Modal Dialog on top of Dynamic Created Form? (CreateParams - overridden)

I am dynamically creating a Form that overrides the CreateParams so that I can have it displayed on the TaskBar. From the dynamically created Form, I am calling a TColorDialog but once it is displayed my Form will go under the MainForm with the ColorDialog on top of that.
After the ColorDialog is closed the dynamic Form will return back over the MainForm.
I see that on the ColorDialog Execute method there is a Handle that can be passed but I am not sure if I am on the right track with that?
If I click under the Dialog on the MainForm it will flash but how can I have the dynamically created Form "own" this Dialog with the MainForm at the back?
I create the Form like this:
procedure TMain.Button1Click(Sender: TObject);
var
SEMArcF: TWriteSEMArcFrm;
begin
SEMArcF := TWRiteSEMArcFrm.Create(nil);
SEMArcF.Show;
end;
and it is freed on the OnClose Event:
procedure TWriteSEMArcFrm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
end;
I am overriding the CreateParams like this:
procedure TWriteSEMArcFrm.CreateParams(var Params: TCreateParams);
begin
inherited;
if (FormStyle = fsNormal) then begin
Params.ExStyle := Params.ExStyle or WS_EX_APPWINDOW;
Params.WndParent := GetDesktopWindow;
end;
end;
and to show the ColorDialog I either create it or just have a TColorDialog Component on the Form, either way will result in the same. I want it to be owned by the dynamic Form.
EDIT
I now add:
Application.ModalPopupMode := pmAuto;
The full code:
procedure TWriteSEMArcFrm.btnBackColourClick(Sender: TObject);
var
ColorDlg: TColorDialog;
begin
Application.ModalPopupMode := pmAuto;
ColorDlg := TColorDialog.Create(nil);
try
if ColorDlg.Execute then
re.Color := ColorDlg.Color;
finally
ColorDlg.Free;
end;
end;
This works fine but could there be any unusual behaviour by setting this?
Thank you
Chris
TColorDialog derives from TCommonDialog, which has two overloaded versions of Execute() available - the legacy parameterless version that has existed for years, and a newer overload that takes a parent HWND as an input parameter. You are likely calling the former. That overload uses the Handle property of the currently active TForm (only if the TApplication.ModalPopupMode property is not set to pmNone), falling back to the Handle of the MainForm if needed. If you want more control, you should call the other overload directly instead, then you can pass the dynamic form's Handle property as the parameter value.

Showing MDI form as modal

This will sound against the nature of MDI.. I need to show a MDI form (FormStyle=fsMdiChild) as modal sometimes. And I also need to access the part between Application.CreateForm and OnShow event of another MDI form, i.e.
Application.CreateForm(Form2,TForm2); // but don't set form2's visible property true.
Form2.caption:='not working example';
Form2.SomeMagicToSetVisibleTrue;
Any ideas?
For your first problem: Add another constructor, for example CreateAsMDI, like this:
constructor TModalAndMDIForm.CreateAsMDI(AOwner: TComponent);
begin
f_blChild := true;
GlobalNameSpace.BeginWrite;
try
inherited CreateNew(AOwner);
if(not(csDesigning in ComponentState)) then begin
Include(FFormState, fsCreating);
try
FormStyle := fsMDIChild;
if(not(InitInheritedComponent(self, TForm))) then
raise Exception.CreateFmt('Can't create %s as MDI child', [ClassName]);
finally
Exclude(FFormState, fsCreating);
end;
end;
finally
GlobalNameSpace.EndWrite;
end;
end;
In the normal constructor just set the variable f_blChild to false and call the inherited create.
You need two more things, rather self explaining:
procedure TModalAndMDIForm.Loaded;
begin
inherited;
if(f_blChild) then
Position := poDefault
else begin
Position := poOwnerFormCenter;
BorderStyle := bsDialog;
end;
end;
//-----------------------------------------------------------------------------
procedure TModalAndMDIForm.DoClose(var Action: TCloseAction);
begin
if(f_blChild) then
Action := caFree;
inherited DoClose(Action);
end;
Now you can call the form modal, if created with the standard constructor, and as MDI child, if created with CreateAsMDI.
If you include this in your form's declaration
property IsChild: boolean read f_blChild;
you can even do things depending on whether the form is an MDI child or not, just interrogating the isChild property.
As for your second problem: do not use Application.CreateForm, but create your form yourself:
Here the two creations for modal and MDI:
//Modal
frmDialog := TMyForm.Create(self);
// Your Code
frmDialog.ShowModal;
frmDialog.Release;
//MDI-Child
frmDialog := TMyForm.CreateChild(self);
// Your code
frmDialog.Show;
I have translated this answer form an article on the site DelphiPraxis.
The simplest method is to create a trivial subclass of the form, and set
FormStyle = fsMDIChild
AND
Form.Visible = False
in the property inspector. This is tried and tested!
At least for Delphi 2007 and 2009 creating the MDI child form invisible is easy. For the early Delphi versions (where it was impossible to set Visible to False in the property inspector) you just have to provide a handler for the OnCreate event and access a protected field of the class:
procedure TMDIChild.FormCreate(Sender: TObject);
begin
FFormState := FFormState - [fsVisible];
end;
This will disable the automatic showing of the MDI child. After you are done with your other initialisations you can simply Show it or set Visible to True.
I will not try to answer your question about modal MDI child forms, as this violates the conventions of the Windows platform.
No answers above actually does the job required. The best answer is wrong as well, because of following:
Opening the first form;
Maximize it;
Now say this form calls another form (mdi);
When it is constructed the same way you will get buggy layout... (it will mdi child, but not maximized, however the first will be still maximized). So a buggy state.
If you really need to decide in run-time whether it's a fsMDIChild or fsNormal you need to apply following approach.
You have form which is stored as fsNormal in design (or vice-versa);
Override the InitializeNewForm method;
Set the value of FFormStyle to fsMDIChild (as shown below).
...
TYourForm = class(TForm)
...
protected
procedure InitializeNewForm; override;
...
procedure TYourForm.InitializeNewForm;
var
FS: ^TFormStyle;
begin
inherited;
if DoYourCheckForMDI then
begin
FS := #FormStyle;
FS^ := fsMDIChild;
end;
end;

How do do things during Delphi form startup

I have a form one which I want to show a file open dialog box before the full form opens.
I already found that I can't do UI related stuff in FormShow, but it seems that I can in FormActivate (which I protect from being called a second time...)
However, if the user cancels out of the file open dialog, I want to close the form without proceeding.
But, a form close in the activate event handler generates an error that I can't change the visibility of the form.
So how does one do some UI related operation during form start up and then perhaps abort the form (or am I trying to stuff a function into the form that should be in another form?)
TIA
It would be best (i think) to show the file open dialog BEFORE you create and show the form. If you want to keep all code together you might add a public class procedure OpenForm() or something:
class procedure TForm1.OpenForm( ... );
var
O: TOpenDialog;
F: TForm1;
begin
O := TOpenDialog.Create();
try
// set O properties.
if not O.Execute then Exit
F := TForm1.Create( nil );
try
F.Filename := O.FIlename;
F.ShowModal();
finally
F.Free();
end;
finally
O.Free();
end;
end;
Set a variable as a condition of the opendialog and close the form on the formshow event if the flag is not set correctly.
procedure TForm1.FormCreate(Sender: TObject);
begin
ToClose := not OpenDialog1.Execute;
end;
procedure TForm1.FormShow(Sender: TObject);
begin
if ToClose then Close();
end;
or even more simply
procedure TForm1.FormShow(Sender: TObject);
begin
if not OpenDialog1.Execute then Close();
end;
If you want to keep the logic conditioning the opening self-contained in the Form, you can put a TOpenDialog in your Form and use a code like this in your OnShow event:
procedure TForm2.FormShow(Sender: TObject);
begin
if OpenDialog1.Execute(Handle) then
Color := clBlue
else
PostMessage(Handle, WM_CLOSE, 0, 0); // NB: to avoid any visual glitch use AlpaBlend
end;
If you don't need this encapsulation, a better alternative can be to check the condition before trying to show the form, for instance by embedding the Form2.Show call in a function that tests all the required conditions first.
Two Ways....
1. using oncreate and onactivate
create a global flag or even 2
var
aInitialized:boolean;
Set the flag to false in the oncreate handler.
aInitialized := false; //we have not performed our special code yet.
Inside onActivate have something like this
if not aInitialized then
begin
//our one time init code. special stuff or whatever
If successful
then set aInitialized := true
else aInitialized := false
end;
And how to close it without showing anything just add your terminate to the formshow. of course you need to test for some reason to close.. :)
Procedure Tmaindlg.FormShow(Sender: TObject);
Begin
If (shareware1.Sharestatus = ssExpired) or (shareware1.Sharestatus = ssTampered) Then
application.Terminate;
End;
In your DPR you will need to add a splash screen type effect. In my case I am showing progress as the application starts. You could also just show the form and get some data.
Code from the splash.pas
Procedure tsplashform.bumpit(str: string);
Begin
label2.Caption := str;
gauge1.progress := gauge1.progress + trunc(100 / items);
update;
If gauge1.progress >= items * (trunc(100 / items)) Then Close;
End;
Program Billing;
uses
Forms,
main in 'main.pas' {maindlg},
Splash in 'splash.pas' {splashform};
{$R *.RES}
Begin
Application.Initialize;
Application.Title := 'Billing Manager';
SplashForm := TSplashForm.Create(Application);
SplashForm.Show;
SplashForm.Update;
splash.items := 5;
SplashForm.bumpit('Loading Main...');
Application.CreateForm(Tmaindlg, maindlg);
SplashForm.bumpit('Loading Datamodule...');
Application.CreateForm(TfrmSingleWorkorder, frmSingleWorkorder);
SplashForm.bumpit('Loading SQL Builder...');
Application.CreateForm(TDm, Dm);
SplashForm.bumpit('Loading Security...');
Application.CreateForm(TSQLForm, SQLForm);
SplashForm.bumpit('Loading Reports...');
Application.CreateForm(Tpickrptdlg, pickrptdlg);
Application.Run;
End.

Resources