Entwickler-Ecke

Grafische Benutzeroberflächen (VCL & FireMonkey) - Zugriff auf Komponenten eines MDIChilds


chrissi1968 - Do 04.11.10 19:33
Titel: Zugriff auf Komponenten eines MDIChilds
Hallo,

ich habe folgendes Problem:
In einer MDI-Anwendung erzeuge ich ein MDIChild. Nun möchte ich aus dem MDIParent (Oder einem anderen MDIChild) eine procedure innerhalb dieses Childs aufrufen. Das klappt auch, nur erhalte ich eine Zugriffsverletzung wenn ich innerhalb dieser procedure eine Komponente aufrufe welche sich auf dem Child-Formular befindet.

Hat jemand von Euch eine Idee woran dies liegen könnte bzw. wo man sowas nachlesen kann?

Vielen Dank und viele Grüße

Chrissi


bummi - Do 04.11.10 19:38

wird schwierig ohne Code...


chrissi1968 - Do 04.11.10 22:33

Hallo,

hier der Code:

1) Erzeugung der Child-Formulare:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
procedure TFMain.CreateMDIChild(frm: TFormClass);
var
  i       : Integer;
  bCreated: Boolean;
  fForm   : TForm;

begin
  { ein neues untergeordnetes MDI-Fenster erstellen }
  bCreated:=False;
  for i := 0 to FMain.MDIChildCount - 1 do
  begin
    if FMain.MDIChildren[i] is frm then
    begin
      fForm:=FMain.MDIChildren[i] as frm;
      bCreated:=True;
      Break;
    end;
  end;
  if not bCreated then
    frm.Create(Application)
  else
  begin
    FForm.ActiveMDIChild;
    FForm.Show;
  end;
end;

Hier der Aufruf:
....
CreateMDIChild(TFVPlan);
....

2) Die function innerhalb des Child:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
procedure TFVPlan.ScrollToNodeInCalendar(iMachine: Integer; iOrder: Integer);
var
  dtBeginn: TDateTime;
  iNode   : IVcNode;

begin
  // Die nachfolgende Codezeile führt zum Fehler:
  iNode:=VcPlan.GetNodeByID(IntToStr(iMachine) + '|' + IntToStr(iOrder));
  if iNode <> nil then
  begin
    dtBeginn:=iNode.DataField[4];
    VcPlan.ScrollToNode(iNode, vcLeftAligned);
    VcPlan.ScrollToDate(dtBeginn, vcLeftAligned, 2);
    iNode.MarkNode:=True;
  end;
end;

3) Und hier der Aufruf aus einem anderen Child:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
procedure TFVPlanningData.TBSelectionFindClick(Sender: TObject);
var
  iMachine: Integer;
  iOrder  : Integer;
  i: Integer;
begin
  iMachine:=DSSQLPlanDB.DataSet.FieldByName('P_Maschine').AsInteger;
  iOrder:=DSSQLPlanDB.DataSet.FieldByName('B_Auftrag').AsInteger;
  for i := 0 to FMain.MDIChildCount - 1 do
  if FMain.MDIChildren[i] is TForm then
  begin
    if TForm(FMain.MDIChildren[i]).Name = 'FVPlan' then
    begin
      FMain.MDIChildren[i].BringToFront;
      FMain.MDIChildren[i].SetFocus;
      FVPlan.ScrollToNodeInCalendar(iMachine, iOrder);
      break;
    end;
  end;
end;

Dies ist meine erste MDI-Applikation, habe also von MDI keine Ahnung! In der Applikation werden Aufträge graphisch dargestellt und es wäre schön wenn man von dem Datengrid (Child1) durch anklicken eines Auftrages diesen im graphischen Grid (Child2) sehen könnte.

Krachen tut's hier:

Delphi-Quelltext
1:
2:
3:
  // Die nachfolgende Codezeile führt zum Fehler:
    iNode:=VcPlan.GetNodeByID(IntToStr(iMachine) + '|' + IntToStr(iOrder));
  // Dabei ist dieses iNode sozusagen eine Komponente von Child2.

Wird die function innerhalb vom Formular selbst aufgerufen funzt's einwandfrei!

Ich habe auch mal versucht dies mit zwei einfachen Formularen und zwei Buttons darzustellen. Auch hier das gleiche Problem!

Vielen Dank und viele Grüße

Chrissi


jaenicke - Do 04.11.10 22:55

Vermutlich ist VcPlan nicht initialisiert. Das siehst du aber ja im Debugger.

Bei welcher Adresse passiert denn die Schutzverletzung? Wenn das Adresse z.B. 20 oder sowas in der Größenordnung ist, dann ist das Objekt nicht initialisiert.


bummi - Do 04.11.10 23:49

ich vermute die böse Ecke ist
FVPlan.ScrollToNodeInCalendar(iMachine, iOrder);

wenn ich es richtig verstanden werden die MDI's ohne Zuweisung erzeugt es wäre die Frage was in FVPlan steht.


Delphi-Quelltext
1:
2:
  if not bCreated then
    frm.Create(Application


chrissi1968 - Fr 05.11.10 11:29

Hallo,

zunächst schon mal herzlichen Dank für Eure Hilfe!

Ich habe auch mal versucht diesen Fehler an einem einfachen Beispiel zu simulieren:
In einer MDI-Anwendung gibt es zwei Formulare:
- Eines welches als "fsNormal" und
- eines welches als "fsMDIChild" erzeugt wird

Auf beiden Formularen gibt es einen Button. Nach der Erzeugung der Formulare frage ich aus dem "fsMDIForm" den caption des Buttons durch eine procedure innerhalb des Formulars ab:

1) Erzeugung von fsMDIChild:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
procedure TFMainForm.CreateMDIChild(frm: TFormClass);
var
  i       : Integer;
  bCreated: Boolean;
  fForm   : TForm;
  Child: TFTest1;

begin
  { ein neues untergeordnetes MDI-Fenster erstellen }
  bCreated:=False;
  for i := 0 to FMainForm.MDIChildCount - 1 do
  begin
    if FMainForm.MDIChildren[i] is frm then
    begin
      fForm:=FMainForm.MDIChildren[i] as frm;
      bCreated:=True;
      Break;
    end;
  end;
  if not bCreated then
  begin
    frm.Create(Application)
  end
  else
  begin
    FForm.ActiveMDIChild;
    FForm.Show;
  end;
end;


2) Erzeugung von fsNormal:


Delphi-Quelltext
1:
2:
3:
4:
5:
procedure TFMainForm.Formular21Click(Sender: TObject);
begin
  Application.CreateForm(TFTest2, FTest2);
  FTest2.Show;
end;


3) Abfrage im MDIChild:


Delphi-Quelltext
1:
2:
3:
4:
procedure TFTest1.KomponenteTest1;
begin
  ShowMessage(Button1.Caption);
end;


--> Zugriffsverletzung bei Adresse 004728D1 in Modul "MDIAPP.exe". Lesen von Adresse 00000360.

4) Abfrage im "Normalen" Fenster:

--> Funzt!

Die Zugriffsverletzung in meinem eigentlichen Projekt lautet:

Zugriffsverletzung bei Adresse 008534A2 in Modul "MDIAPP.exe". Lesen von Adresse 00000394.

Dieses VcPlan ist eine ActiveX-Komponente. Ich habe versucht an dieser Stelle nur mal die caption eines Buttons abzufragen:

.....
ShowMessage(TBExit.Caption);
--> Kracht genau so!!!!
iNode:=VcPlan.GetNodeByID(IntToStr(iMachine) + '|' + IntToStr(iOrder));
.....

Zugriffsverletzung bei Adresse 00853465 in Modul "CkPlan.exe". Lesen von Adresse 0000036C.

Es scheint mir so als ob ich auf die einzelnen Objekte meines Child-Formulars keinen Zugriff haben wenn ich dieser Zugriff von außen kommt. Bei einem "normalen" Formular klappt das einwandfrei!

Viele Grüße

Chrissi


jaenicke - Fr 05.11.10 11:48

user profile iconchrissi1968 hat folgendes geschrieben Zum zitierten Posting springen:

Delphi-Quelltext
1:
    frm.Create(Application)                    
Aua...
Du rufst die Methode Create des nicht existierenden Objekts frm auf...
Wie war das nochmal mit einem Konstruktoraufruf? ;-)


chrissi1968 - Fr 05.11.10 12:10

Ups....... :autsch: :autsch: :autsch: :autsch: :autsch: :autsch:

Vielen herzlichen Dank!
Das kommt davon wenn man aus Planlosigkeit einen Code abtippt!

Funzt jetzt supergut und einwandfrei!!

Viele Grüße

Chrissi


SvenAbeln - Fr 05.11.10 12:16

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconchrissi1968 hat folgendes geschrieben Zum zitierten Posting springen:

Delphi-Quelltext
1:
    frm.Create(Application)                    
Aua...
Du rufst die Methode Create des nicht existierenden Objekts frm auf...
Wie war das nochmal mit einem Konstruktoraufruf? ;-)


frm ist hier aber doch kein Objekt sondern eine Klasse:

Delphi-Quelltext
1:
procedure TFMainForm.CreateMDIChild(frm: TFormClass);                    


jaenicke - Fr 05.11.10 12:22

Stimmt, da habe ich wiederum nicht drauf geachtet. Der Name ist dann ja auch sehr passend gewählt... :roll:

Die Stelle ist aber trotzdem die richtige. Denn das Ergebnis wurde weggeworfen, ich vermute mal das hat er jetzt auch korrigiert in:

Delphi-Quelltext
1:
fForm := frm.Create(Application);                    


chrissi1968 - Fr 05.11.10 13:19

Kleiner Nachtrag:

Habe die function für das Erzeugen der Childs wie folgt geändert:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
procedure TFMain.CreateMDIChild(frmClass: TFormClass; var refObj);
var
  i       : Integer;
  bCreated: Boolean;
  fForm   : TForm;

begin
  { ein neues untergeordnetes MDI-Fenster erstellen }
  bCreated:=False;
  for i := 0 to FMain.MDIChildCount - 1 do
  begin
    if FMain.MDIChildren[i] is frmClass then
    begin
      fForm:=FMain.MDIChildren[i] as frmClass;
      bCreated:=True;
      Break;
    end;
  end;
  if not bCreated then
  begin
//      frmClass.Create(Application)
    Application.CreateForm(frmClass, refObj);
  end
  else
  begin
    TForm(fForm).ActiveMDIChild;
    TForm(fForm).Show;
  end;
end;


Der Aufruf erfolgt dann:

CreateMDIChild(TFVPlan, FVPlan);

Vielen Dank nochmal und viele Grüße

Chrissi


bummi - Fr 05.11.10 13:27

wäre es wie unten stehen nicht intuitiver und universeller einsetzbar, falls Du doch mal 2 Forms der Selben Klassse benötigst?


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
procedure TForm1.CreateMDIChild(frmClass: TFormClass; var refObj);
var
  i       : Integer;
  bCreated: Boolean;
begin
  bCreated:=False;
  for i := 0 to MDIChildCount - 1 do
  begin
    if MDIChildren[i]=Tform(refObj) then
    begin
      bCreated:=True;
      Break;
    end;
  end;
  if not bCreated then
  begin
    Application.CreateForm(frmClass, refObj);
  end
  else
  begin
    TForm(refObj).ActiveMDIChild;
    TForm(refObj).Show;
  end;
end;


chrissi1968 - Fr 05.11.10 15:00

Hallo brummi,

ja, Deine Lösung ist besser!! :lol: :lol:

Viele Grüße

Chrissi