I am creating an application where the main form is basically a menu (in a split view) and a pagecontrol with a "home tab" on it. Based on the menu item selected, a new tabsheet is created with an embedded form on it, where the user can perform numerous actions and data look ups, etc. This is all working great, except I have one problem embedded form, whose FormShow method is being triggered twice when a new tab is created for this form.
To create the problem form, I use:
procedure TMain.actMemberExecute(Sender: TObject);
var
NewTabSheet : TTabSheet;
NewMembForm : TMembForm;
begin
CloseSVMenu;
NewTabSheet := TTabSheet.Create(PageControl1);
NewTabSheet.Visible := true;
NewTabSheet.Caption := 'Member Tab';
NewTabSheet.PageControl := PageControl1;
NewTabSheet.ImageIndex := 18;
NewMembForm := TMembForm.Create(NewTabSheet);
NewMembForm.Parent := NewTabSheet;
NewMembForm.Align := alclient;
NewMembForm.Show;
PageControl1.ActivePage := NewTabSheet;
end;
And a "good" form gets created this way (which seems the same, to me)
procedure TMain.actPersonExecute(Sender: TObject);
var
NewTabSheet : TTabSheet;
NewPersForm : TPersInfoForm;
begin
CloseSVMenu;
NewTabSheet := TTabSheet.Create(PageControl1);
NewTabSheet.Visible := true;
NewTabSheet.Caption := 'Person';
NewTabSheet.PageControl := PageControl1;
NewTabSheet.ImageIndex := 17;
NewPersForm := TPersInfoForm.Create(NewTabSheet);
NewPersForm.Parent := NewTabSheet;
NewPersForm.Align := alClient;
NewPersForm.Show;
PageControl1.ActivePage := NewTabSheet;
end;
Each of the forms have similar OnCreate and OnShow form events. The FormCreate methods set make sure any required lookup tables in the data module are open and then set each of the form's TAction.Enabled
status' based on the user's permissions to them. The FormShow simply positions the form's primary table to the first record ( <tdataset>.First
), and then checks for some other pre-processing before the user sees the data for that form (such as if it needs to move to a specific record, via <tdataset>.Locate(...)
).
I have traced through the actions of multiple forms. All go through the Create procedure once (as expected), returns to procedure in the Main form, and then FormShow fires. The procedure completes, and then with the problem form, the entire screen blinks, and FormShow starts again. The other 8 forms, all based on the same code, work as expected, with FormShow called once and no screen blinking.
Here are snippets of the FormCreate and FormShow procedures for the same two samples above
Problem form (TMembForm)
procedure TMembForm.FormCreate(Sender: TObject);
var
CategoryLabel : string;
begin
OpenToMemberID := 0;
CreateNewMember := false;
fdqMembList.active := true;
dm1.fdtMemCat.Active := true;
dm1.fdtMemStatus.Active := true;
dm1.fdtMemStatus.First;
dm1.fdtMemStatus.Next;
cbFilterList.Items.Clear;
cbFilterList.Items.Add('All');
while not dm1.fdtMemStatus.Eof do
begin
CategoryLabel := dm1.fdtMemStatus.FieldByName('MemStatDesc').AsString;
cbFilterList.Items.Add(CategoryLabel);
dm1.fdtMemStatus.Next;
end;
cbFilterList.Items.Add('Other');
cbFilterList.ItemIndex := 1;
fdqMembList.Filter := 'MemStatID=1';
fdqMembList.Filtered := true;
cpgMembers.CollapseAll;
cpMemBasic.Collapsed := false;
{set action.enabled here....}
end;
procedure TMembForm.FormShow(Sender: TObject);
var
StatID, NMResult : integer;
NewMemStat, NewMemPrime, NewMembID : integer;
NewMembDlg : TNewMembDlg;
begin
{whatever pre-processing}
OpenToMemberID := MembOpen;
CreateNewMember := CreateNewMemb;
fdqMembList.First;
{check if need to immediately create new member}
if CreateNewMember then
begin
CreateNewMember := false;
CreateNewMemb := false;
NewMembDlg := TNewMembDlg.Create(nil);
try
NMResult := NewMembDlg.ShowModal;
if NMResult=mrOK then
begin
NewMembID := dm1.dbMain.ExecSQLScalar('select max(memberno)+1 from member');
NewMemStat := NewMembDlg.NewMemStat;
NewMemPrime := NewMembDlg.NewMemPrime;
fdqNewMemb.Close;
fdqNewMemb.ParamByName('MembID').AsInteger := NewMembID;
fdqNewMemb.ParamByName('StatID').AsInteger := NewMemStat;
fdqNewMemb.ExecSQL;
fdqNewMembPrime.Close;
fdqNewMembPrime.ParamByName('MembID').AsInteger := NewMembID;
fdqNewMembPrime.ParamByName('PersID').AsInteger := NewMemPrime;
fdqNewMembPrime.ExecSQL;
OpenToMemberID := NewMembID;
fdqMembList.Refresh;
end;
finally
NewMembDlg.Free;
end;
end;
{check if open to specific member}
if OpentoMemberID>0 then
begin
fdqMembList.Filtered := false; {need to be able to find any}
if fdqMembList.Locate('MemberNo',OpenToMemberID,[]) then
begin
fdqMembList.Filtered := false;
StatID := fdqMembList.FieldByName('MemStatID').AsInteger ;
fdqMembList.Filter := 'MemStatID='+StatID.ToString;
if StatID=0 then cbFilterList.ItemIndex := cbFilterlist.Items.Count - 1
else cbFilterList.ItemIndex := StatID;
fdqMembList.Filtered := true;
end;
OpenToMemberID := 0;
MembOpen := 0;
end;
end;
And here is a good form (TPersInfoForm)
procedure TPersInfoForm.FormCreate(Sender: TObject);
var
CategoryLabel : string;
begin
fdtPerson.Active := true; {make sure table is open}
fdtPersMile.Active := true;
fdtPersContact.Active := true;
dm1.fdtMilestones.Active := true;
{populate combo box with person category lables}
cbFilterList.Items.Clear;
cbFilterList.Items.Add('All');
dm1.fdtPersonCat.Active := true;
dm1.fdtPersonCat.First; {now skip 0}
dm1.fdtPersonCat.Next;
dm1.fdtContactType.Active := true;
dm1.fdtPersRelateType.Active := true;
dm1.fdtActivityCat.Active := true;
while not dm1.fdtPersonCat.Eof do
begin
CategoryLabel := dm1.fdtPersonCat.FieldByName('PersCatDesc').AsString;
cbFilterList.Items.Add(CategoryLabel);
dm1.fdtPersonCat.Next;
end;
cbFilterList.Items.Add('Other');
cbFilterList.ItemIndex := 1;
fdtPerson.Filtered := true;
fdtPerson.Filter := 'PersCatID=1';
{set the rest of the permissions for actions}
end;
procedure TPersInfoForm.FormShow(Sender: TObject);
var
CatID, NPresult : integer;
NewPersDlg : TNewPersDlg;
begin
{test if new Person record should be created}
fdtPerson.First;
if CreateNewPerson then
begin
{create an instance of NewPerson form, call modally, then create record}
{new person will always be set to Other, to start}
CreateNewPerson := false;
NewPersDlg := TNewPersDlg.Create(nil);
try
NPResult := NewPersDlg.ShowModal;
if NPResult=mrOK then
begin
fdtPerson.Insert;
fdtPerson.FieldByName('LastName').AsString := NewPersDlg.LastName;
fdtPerson.FieldByName('FirstName').AsString := NewPersDlg.FirstName;
fdtPerson.FieldByName('PersCatID').AsInteger := 0;
fdtPerson.Post;
OpenToPersonID := dm1.dbMain.ExecSQLScalar('select last_insert_id()');
fdtPerson.Filter := 'PersCatID=0';
fdtPerson.Filtered := true;
cbFilterList.ItemIndex := cbFilterList.Items.Count-1;
end;
finally
NewPersDlg.Free;
end;
end;
if OpenToPersonID>0 then
begin
fdtPerson.Filtered := false; {need to allow all}
if fdtPerson.Locate('PersonID',OpenToPersonID,[])then begin
fdtPerson.Filtered := false;
catID := fdtPerson.FieldByName('PersCatID').AsInteger;
fdtPerson.Filter := 'PersCatID='+CatID.ToString;
fdtPerson.Filtered := true;
fdtPerson.Locate('PersonID',OpenToPersonID,[]); {needed to ensure on correct record after re-filtering}
cbFilterList.ItemIndex := CatID;
if CatID=0 then cbFilterList.ItemIndex := cbFilterList.Items.Count-1;
OpenToPersonID := 0; {reset the flag}
end;
end;
end;
Would appreciate any help or ideas someone may have.
Thanks - Rick Brodzinsky
What is the value of the Visible
property of your troublesome Form? If it is True then that would explain why the OnShow
event is firing twice.
The first time, the OnShow
event will fire just after the Form is created, as it is already visible before you make it be a child of the TabSheet.
The second time, it will fire when you call the Form's Show()
method.