Search code examples
delphivcl

FormShow being called twice on embedded form


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


Solution

  • 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.