Autor |
Beitrag |
Gerhard_S
      
Beiträge: 98
|
Verfasst: Sa 19.10.13 00:31
Die Frage soll heißen: Kann ich mit Delphi XE2 von Frame1 aus Frame2 erzeugen und von Frame2 aus Frame3?
Wenn ja, wie tausche ich Daten zwischen den Frames aus? Gibt es dazu Beispielanwendungen oder Tutorials?
|
|
jaenicke
      
Beiträge: 19314
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Sa 19.10.13 07:54
Frames sind normale Klassen. Da gibt es keine speziellen Regeln für den Datenaustausch. Sprich du hast Methodenaufrufe in die eine Richtung und Events in die andere Richtung. Oder du arbeitest z.B. mit Interfaces.
Auch die Kaskadierung ist möglich. Ob das wirklich sinnvoll ist, musst du selbst schauen.
|
|
Gerhard_S 
      
Beiträge: 98
|
Verfasst: Sa 19.10.13 21:37
Ich würde nicht fragen, wenn ich nicht ein ungelöstes Problem hätte. Also:
Ich rufe aus Frame10 den zur Entwurfszeit erstellten Frame16 auf, auf dem sich eine Pagecontrol mit 2 Tabsheets (TabSheet1 und TabSheetPlus)befindet. Auf TabSheet1 liegt ein Webbrowser namens WB.
Der Aufruf in Unit10 mit Frame10 erfolgt so:
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:
| FFrame := TFrame16.Create(Form1); FFrame.Parent := Form1; FFrame.ParentBackground := false; Form1.Width := 865; Form1.Height := 632; Form1.Repaint; TFrame16(FFrame).Width := 865; TFrame16(FFrame).Height := 632; FFrame.Align := alClient; TFrame16(FFrame).BringToFront; TFrame16(FFrame).Edit1.Visible := true; TFrame16(FFrame).Edit1.Text := 'https://www.google.de'; TFrame16(FFrame).Label1.Visible := true; TFrame16(FFrame).PageControl1.OwnerDraw := true; TFrame16(FFrame).ToolButton2.Enabled := false; TFrame16(FFrame).ToolButton3.Enabled := false; TFrame16(FFrame).ToolButton4.Enabled := false; PlusCaption := ' + '; TFrame16(FFrame).TabSheetPlus.Caption := PlusCaption; TFrame16(FFrame).PageControl1.ActivePage := TFrame16(FFrame).TabSheet1; TFrame16(FFrame).WB := TWebbrowser.Create(TFrame16(FFrame).TabSheet1); TFrame16(FFrame).WB.Align := alClient; TFrame16(FFrame).WB.Silent := True; TFrame16(FFrame).WB.Visible := True; if Trim(TFrame16(FFrame).Edit1.Text) <> '' then TFrame16(FFrame).WB.Navigate(TFrame16(FFrame).Edit1.Text); TFrame16(FFrame).WB.OnBeforeNavigate2 := TFrame16(FFrame).BeforeNavigate2; TFrame16(FFrame).WB.OnTitleChange := TFrame16(FFrame).TitleChange; |
Das läuft störungsfrei ab.
In Unit16, die Frame16 beherbergt, läuft aber nichts nach Plan:
Edit1.Text wird angezeigt, TabSheetPlus.Caption nicht. Eine Webseite wird nicht angezeigt, denn bei der Abarbeitung dieser Prozedur (erfolgt als erstes) wird kein Webbrowser gefunden:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| procedure TFrame16.TitleChange(ASender: TObject; const Text: WideString); var i: integer; begin for i:=0 to PageControl1.ActivePage.ControlCount -1 do if PageControl1.ActivePage.Controls[i] is TWebbrowser then begin PageControl1.ActivePage.Caption := Text+' '; end; end; |
Was mache ich falsch?
|
|
jaenicke
      
Beiträge: 19314
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Sa 19.10.13 22:04
Tut mir leid, aber der Quelltext ist schlecht, und das nicht nur wegen der Bezeichnung TFrame16. Frames sind sehr gut geeignet um einzelne Segmente der GUI wiederverwendbar zu kapseln. Du hingegen kapselst nicht wirklich, sondern greifst von außen auf die Elemente des Frames zu. Das führt die Nutzung von Frames teilweise ad absurdum.
Warum suchst du überhaupt nach dem Webbrowser? Du packst den doch in das Feld WB.
Warum er nicht gefunden wird liegt evtl. am Owner. Als Owner übergibst du TabSheet1, dementsprechend kann er auch nur in den Controls von TabSheet1 gefunden werden, suchen tust du aber in ActivePage, was ja nicht notwendigerweise das selbe ist.
Wenn du Interesse daran hast, kannst du auch entweder hier im Thread oder per PN das Projekt insgesamt posten, dann könnte ich mehr dazu sagen wie du das besser machen kannst. Gerade hinsichtlich Kapselung usw., denn an der Stelle solltest du das ganze objektorientierter angehen. Das wäre deutlich einfacher und übersichtlicher.
|
|
Gerhard_S 
      
Beiträge: 98
|
Verfasst: Sa 19.10.13 23:28
Danke für deine Bemühungen. Hier der Auszug aus Unit1, mit dem Frame10 aufgerufen wird:
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: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44:
| private FFrame: TFrame; ... procedure TForm1.NebenspalteA0Click(Sender: TObject); begin if FFrame <> nil then FFrame.Free; FFrame := TFrame10.Create(Panel1); FFrame.Align := alClient; FFrame.Parent := Form1.Panel1; Form1.Panel1.Height:= Form1.ClientHeight; with TFrame10(FFrame).ComboBox1 do begin Items.Add('Nur Bild'); Items.Add('Nur Text'); Items.Add('Liste'); Items.Add('Bild mit Überschrift'); Items.Add('Bild und Text'); end; TFrame10(FFrame).ComboBox1.Text:=TFrame10(FFrame).ComboBox1.Items[0]; TFrame10(FFrame).Edit1.Visible := false; TFrame10(FFrame).RichEdit1.Visible := false; TFrame10(FFrame).Label2.Visible := false; TFrame10(FFrame).btnTest.Visible := false; TFrame10(FFrame).Label2.Top := TFrame10(FFrame).btnFindPicture.Top + 25; TFrame10(FFrame).lblURL.Visible := false; TFrame10(FFrame).Label3.Visible := false; TFrame10(FFrame).btnItalic.Visible := false; TFrame10(FFrame).btnFindPicture.Left := TFrame10(FFrame).ComboBox1.Left; TFrame10(FFrame).btnFindPicture.Top := TFrame10(FFrame).ComboBox1.Top+ TFrame10(FFrame).ComboBox1.Height + 25 ; TFrame10(FFrame).btnFindPicture.Caption := '&Bild auswählen'; TFrame10(FFrame).Image1.Top := TFrame10(FFrame).btnFindPicture.Top +TFrame10(FFrame).btnFindPicture.Height + 25; TFrame10(FFrame).Image1.Width := 250; TFrame10(FFrame).Image1.Height := 250; TFrame10(FFrame).btnStartBrowser.Visible := false; TFrame10(FFrame).btnCancel.Visible := false; TFrame10(FFrame).btnSave.Visible := false; TFrame10(FFrame).lblNewWidth.Visible := false; TFrame10(FFrame).lblNewHeight.Visible := false; TFrame10(FFrame).lblaltText.Visible := false; TFrame10(FFrame).edalttxt.Visible := false; end; |
Unit10 und Unit16 incl. dfm-Dateien habe ich als Datei angehängt.
Einloggen, um Attachments anzusehen!
|
|
jaenicke
      
Beiträge: 19314
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: So 20.10.13 09:22
Das wird etwas länger.
- try..finally
Delphi-Quelltext 1: 2: 3: 4: 5: 6:
| try srcGif := TGifImage.Create; ... finally srcGif.Free; end; | Das ist falsch, richtig ist es so: Delphi-Quelltext 1: 2: 3: 4: 5: 6:
| srcGif := TGifImage.Create; try ... finally srcGif.Free; end; | Grund:
Wenn im Konstruktor eine Exception passiert, wird die Variable srcGif gar nicht zugewiesen. Dann fliegst du ja aus der Prozedur heraus. Durch deine Variante kommst du aber trotzdem im finally an. Obwohl da im besten Fall nil drin steht (das wäre in Ordnung), im schlechtesten aber ein nicht initialisierter Wert (ja, du initialisierst die Werte, das könntest du dir aber dann sparen).
- Parameterreihenfolge
Du übergibst an calcdim erst die Höhe, dann die Breite, sonst überall (wie auch in Delphi selbst) erst Breite dann Höhe. Das provoziert Flüchtigkeitsfehler, besser ist es sich danach zu richten wie es in Delphi ist und das auch überall so zu machen.
- Fallunterscheidung nach Bildtyp
Du machst bei jedem Bildtyp ohnehin das gleiche. Mit Hilfe von TPicture kannst du die Erkennung des Bildtyps Delphi überlassen und so gleich alle von Delphi unterstützten behandeln. Zudem wird der Quelltext deutlich kürzer. Sprich er sieht nur noch so aus: 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: 30: 31: 32: 33: 34:
| procedure TFrame10.btnFindPictureClick(Sender: TObject); var MaxWidth, MaxHeight, NewWidth, NewHeight: integer; enctype: TEncoderType; SrcPicture, DestPicture: TPicture; Smooth: Boolean; begin Smooth := true; MaxWidth := 250; MaxHeight := 300; NewWidth := 1; NewHeight := 1; Image1.AutoSize := true; OpenPictureDialog1.Filter := 'Bilddateien|*.bmp;*.gif;*.jpg;*.png;'; if OpenPictureDialog1.Execute then begin Image1.Left := btnFindPicture.Left; SrcPicture := TPicture.Create; try DestPicture := TPicture.Create; try SrcPicture.LoadFromFile(OpenPictureDialog1.FileName); calcdim(SrcPicture.Height, SrcPicture.Width, MaxHeight, MaxWidth, NewHeight, NewWidth); StretchGraphic(SrcPicture.Graphic, DestPicture.Graphic, NewWidth, NewHeight, Smooth); Image1.Picture.Assign(DestPicture); finally DestPicture.Free; end; finally SrcPicture.Free; end; lblNewWidth.Caption := lblNewWidth.Caption + ' ' + IntToStr(NewWidth); ... |
- Anordnung der Komponenten
Du setzt viele Positionen manuell. Bist du sicher, dass du da nicht besser mit Anchors arbeiten kannst?
- Width <> ClientWidth
Du setzt nach dem Erzeugen des Frames Form1.Width auf 865 und danach die des Frames ebenso. Die Breite des Formulars schließt aber den Rand mit ein. Der Frame hängt so rechts über das Formular hinaus.
Setze besser ClientWidth des Formulars, dann ist der Clientbereich so groß wie du es möchtest und der Rand des Formulars kommt dazu.
- Kapselung
Du greifst von außen auf Elemente des Frames zu, die eigentlich nur den Frame selbst betreffen. Dadurch wird der Quelltext unübersichtlicher und schlechter wartbar, denn:
- Wenn du wissen willst was mit einem bestimmten Element gemacht wird, musst du den gesamten Quelltext durchschauen statt nur den des Frames.
- Beim Zugriff musst du jedesmal den Frame davor schreiben, was die Lesbarkeit auch nicht erhöht.
- Wenn du eine Komponente austauschst, z.B. um die Funktionalität zu erweitern, musst du nicht nur den Frame ändern, sondern ggf. auch weiteren Quelltext.
Deshalb ist es sinnvoller die Funktionalität zu kapseln und von außen nur durch neutrale Funktionen, die nicht von der internen Umsetzung abhängen, zuzugreifen. Erzeugung des Frames 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:
| procedure TFrame10.btnStartBrowserClick(Sender: TObject); var FFrame: TFrame; begin FFrame := TFrame16.Create(Form1); FFrame.Parent := Form1; FFrame.ParentBackground := false; Form1.Width := 865; Form1.Height := 632; Form1.Repaint; FFrame.Width := 865; FFrame.Height := 632; FFrame.Align := alClient; FFrame.BringToFront; TFrame16(FFrame).InitBrowser('https://www.google.de'); end; | Im Frame 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:
| public procedure InitBrowser(const AUrl: string); end;
procedure TFrame16.InitBrowser(const AUrl: string); var PlusCaption: string; begin Edit1.Visible := true; Edit1.Text := AUrl; Label1.Visible := true; PageControl1.OwnerDraw := true; ToolButton2.Enabled := false; ToolButton3.Enabled := false; ToolButton4.Enabled := false; PlusCaption := ' + '; TabSheetPlus.Caption := PlusCaption; PageControl1.ActivePage := TabSheet1; WB := TWebbrowser.Create(TabSheet1); WB.Parent := TabSheet1; WB.Align := alClient; WB.Silent := true; WB.Visible := true; if Trim(AUrl) <> '' then WB.Navigate(AUrl); WB.OnBeforeNavigate2 := BeforeNavigate2; WB.OnTitleChange := TitleChange; end; |
- Komponentenbezeichnungen
Du benennst ja Komponenten durchaus, das ist auch gut. Allerdings solltest du das dann zumindest für alle, die im Quelltext verwendet werden, auch durchhalten. Es auf hinterher zu verschieben ist keine gute Idee, weil man es entweder dann gar nicht mehr macht oder flucht, weil man erstens manchmal erst selbst nach dem Zweck schauen muss und zweitens auf jeden Fall den Quelltext überall ändern muss.
- Webbrowser finden
Ich gehe davon aus, dass du mehrere Tabs mit mehreren Webbrowsern nutzen willst, und deshalb den Browser im Frame suchen willst, richtig?
Dann macht es mehr Sinn einen gemeinsamen Vorfahren als Nachfahre von TFrame zu erstellen, der zuerst die Funktionalität aller Frames kapselt und davon einen abzuleiten, der die Browserfunktionen kapselt. Dann brauchst du nur ein TDictionary<TTabSheet, TFrame>, in dem du die Framezuordnungen speicherst, kannst dann ActivePage dort nachschlagen und dann mit dem Frame direkt arbeiten.
Den kannst du dann mit is auf den Typ TBrowserFrame oder wie du den dann nennst prüfen und kannst ggf. darauf casten und direkt damit arbeiten.
Warum es nicht funktioniert:
Auf dem Tabsheet liegt der Frame und darin der TWebBrowser. Wenn du so suchen willst, musst du zuerst den Frame suchen und darin den TWebBrowser.
Ich denke mal das ist erst einmal genug. 
|
|
Gerhard_S 
      
Beiträge: 98
|
Verfasst: So 20.10.13 23:42
Danke für die freundliche Hilfe. Jetzt klappt's auch bei mir.
|
|
|