Fog Creek Software
Discussion Board




MDI Programming

Hi
I'm trying to built an MDI app with a settings dialog.
I want the selections in the dialog to change component properties on the
currently active MDIChild form.
When I try to access the components on the child form from  the parent form
or the settings dialog, I get an access violation. However, I can change
component properties from the child form .

I'm just not catching something, What am I doing wrong?

NPG
Thursday, October 10, 2002

Why are you doing that? Sounds weird.

Anyway, show us a *small* snippet of code how you're accessing the controls on the active form.

Aaron Lawrence
Friday, October 11, 2002

I am a little confused here; Is this what you want to accomplish?

This updates the currently active MDI-child from the parent
form with the selected color from the color-dialog.

Could you explain more in detail what you need to do?


procedure TForm1.Button1Click(Sender: TObject);

begin
  If ColorDialog1.Execute then begin
    TForm(ActiveMDIChild).Color:=ColorDialog1.Color;
  end;
end;

Patrik
Friday, October 11, 2002


begin
  If ColorDialog1.Execute then begin
    frmMDI.ActiveMDIChild.Color := clred

end;

This works fine for me but you should check if you have a mdiChild form first.

Chris Thompson
Monday, October 14, 2002

>>  If ColorDialog1.Execute then begin
>>    frmMDI.ActiveMDIChild.Color := clred

That's VERY DANGEROUS code - it relies on the fact that there's a pre-defined "frmMDI" global variable in your unit, and it does NOT take into account that you might have multiple instances of the TfrmMDI form class! Bad bad bad programming......

You should always use the "MDIChildren" collection on your main form (MDI container form) and iterate over its members, or use the "ActiveMDIChild" property which always points to just one given MDI child.

Marc Scheuner
Wednesday, October 16, 2002

Hm.. frmMDI may be the name of the main form, and if that is the case, isn't *that* bad programming.

The only thing you need to check is ActiveMDI.

BTW, MDIs are a thing of the past. Why would you want to create a small, contained desktop only for your application?

Leonardo Herrera
Friday, October 18, 2002

You can't just make a blanket statement that MDI is a thing of the past.  It still makes sense for some application models.

Access 2002 and Excel 2002 still use a MDI model to contain their windows.

Troy Wolbrink
Saturday, October 19, 2002

Thanks for the replys sorry to not answer sooner
here is an example of what I am trying to do in code

frmMain.ActiveMDIChild.SubMneu.Enabled := False;

When I do this the IDE says that it doesn't know what submenu is. So I tried the following.

frmMDIChild.Submenu.Enabled := Flase;

This compiles fine but when I run that line of code I get an access violation.

The MIDChild is in the uses clause of the Main form & vise versa, So I don't think thats the problem
Thanks

NPG
Monday, October 21, 2002

frmMDIChild is probably an empty variable if the MDI child form is created dynamically (which is recommended). You might want to remove the MDI child form from the list of automatically created forms in the Project Options dialog, and delete the frmMDIChild variable.

Then, you need to cast frmMain.ActiveMDIChild to TfrmMDIChild (I assume) so the compiler knows how to access its properties.

if Assigned(frmMain.ActiveMDIChild) then
  TfrmMDIChild(frmMain.ActiveMDIChild).SubMenu.Enabled ...

Having said this, it's better to use a more advanced mechanism to update the menu items on the MDI child forms, because your approach quickly leads to spaghetti code and confusing interdependencies.

Have a look at the Observer pattern in the classic Design Patterns book, for example.

Frederik Slijkerman
Tuesday, October 22, 2002

NPG,

As Frederik said, you could end up with some strange and confusing interdependencies. One way around this is to create a Customized MDIChild Form base class, example:

----

type
  TBaseMDIForm = class(TForm)
  private
    { Private declarations }
  public
    { Public declarations }
    constructor Create(AOwner : TComponent); override;
    destructor  Destroy; override;
    procedure CustomStuff(Action : String); virtual;
  end;

constructor TBaseMDIForm.Create(AOwner:TComponent);
begin
  CustomStuff('CREATE');
  inherited Create(AOwner);
end;

destructor TBaseMDIForm.Destroy;
begin
  CustomStuff('DESTROY');
  inherited;
end;

procedure TBaseMDIForm.CustomStuff(Action : String);
begin
  if Action='CREATE' then begin
    MessageBox(0,'Im alive','Im alive',MB_OK);
  end;
  if Action='DESTROY' then begin
    MessageBox(0,'Killed','Killed',MB_OK);
  end;
end;

---

And then derive all your MDI child forms in the application, not from the standard TForm, but rather TBaseMDIForm.
This is a rudimentary example only, but adding custom public methods to your base class for controlling the menus on the forms will then be inherited in all your MDI chlidren.

This technique will not result in interdependencies and you will have a smaller portion of spagetthi on your plate.

Have fun,

Patrik
Tuesday, October 22, 2002

Here is Service Pack 1 of my example code :-)

constructor TBaseMDIForm.Create(AOwner:TComponent);
begin
  inherited Create(AOwner);
  CustomStuff('CREATE');
end;


you shoud have the inherited stuff first in your counstructors to avoid strange errors if you assign
values to form properties in your CustomStuff() procedure.

Otherwise your form may not be correctly created before you start assigning values to its properties.

Patrik
Tuesday, October 22, 2002

There is no need to implement observer or any other pattern to centralize command enabling. That's what the TActionList is for, and it does the job beautifully.

Big B
Tuesday, October 22, 2002

*  Recent Topics

*  Fog Creek Home