Eigene von TPage abgeleitete Klasse mit anderen Controls

Rund um die LCL und andere Komponenten
Antworten
Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Eigene von TPage abgeleitete Klasse mit anderen Controls

Beitrag von Socke »

Hallo zusammen,
derzeit bastel ich an einer Datenbankanwendung (SQLite3), in der die einzelnen Records aus der Datenbank auf je einer Seite (TPage) in einem Notebook (TNotebook) darstellen soll. Dazu wollte ich mir eine eigene Klasse von TPage ableiten, die selbstständig (in ihrem constructor) die benötigten Controls also Labels, Editfelder, usw. erstellt.
Die ganze Datenbankanbindung funktioniert soweit und auch die einzelnen Seiten werden erstellt. Jedoch werden die Controls, die dort drauf sein sollten, nicht oder nur teilweise angezeigt.

Mit folgendem Code wird die Seite erstellt und dem Notebook (NbMain) hinzugefügt.
ARecord ist ein ein Pointer auf einen Record, der eine Zeile aus der Datenbank enthält (Pointer deshalb, weil ich nicht immer einen ganzen Record verschieben will; auch wenn es nur 20 Byte sind).

Code: Alles auswählen

var page: TApplicationPage;
begin
    page := TApplicationPage.Create(self,ARecord);
    page.Caption := ARecord^.ApplicationName;
 
    page.Parent := NbMain;
    NbMain.PageList.Add(TPage(page));
end;
Meine Page-Klasse ist folgendermaßen deklariert/implementiert:

Code: Alles auswählen

interface
 
type
// zuerst der Record aus der Datenbank
  PDBApplicationRecord = ^TDBApplicationRecord;
  TDBApplicationRecord = record
    ID: LongInt;
    ApplicationName: String;
    UserName: String;
    RegistrationKey: String;
    Comment: String;
  end; 
 
// dann die seite selbst
  TRecordChange = procedure(Sender: TObject; NewRecord: PDBApplicationRecord) of object;  // ein event handler
  TApplicationPage = class(TPage)
  private
    fRecord: PDBApplicationRecord;
    fRecordChange: TRecordChange;
    function GetRecord: TDBApplicationRecord;
    procedure SetRecordP ( const AValue: PDBApplicationRecord ) ;
  protected
    procedure RecordChanged;
  public
    LabelId: TLabel;
    EditId: TEdit;
    constructor Create(TheOwner: TComponent); override; overload;
    constructor Create(TheOwner: TComponent; PRecord: PDBApplicationRecord); overload;
    function GetId: LongInt;  // gibt die Datenbank-id des Records zurück
    property ApplicationRecordP: PDBApplicationRecord read fRecord write SetRecordP;
    property ApplicationRecord: TDBApplicationRecord read GetRecord;
    property OnRecordChange: TRecordChange read fRecordChange write fRecordChange;
  end; 
  // alles, was deklariert aber noch nicht implementiert ist, habe ich mal raus genommen, wird sowieso nicht aufgerufen/benutzt
 
implementation
 
// Implementation; hier ist auch nur das drinnen, was zu der Klasse gehört und auch aufgerufen/benutzt wird
 
function TApplicationPage.GetRecord: TDBApplicationRecord;
begin
  result := fRecord^;
end;
 
procedure TApplicationPage.SetRecordP ( const AValue: PDBApplicationRecord ) ;
begin
  ShowMessage('Tab SetRecordP: ID_alt - '+IntToStr(fRecord^.ID));
  if fRecord = AValue then exit;
  fRecord := AValue;
  RecordChanged;
  ShowMessage('Tab SetRecordP: ID - '+IntToStr(fRecord^.ID));
  EditId.Caption := IntToStr(fRecord^.ID);
end;
 
procedure TApplicationPage.RecordChanged;
begin
  if Assigned(fRecordChange) then
    fRecordChange(self, fRecord);
end;
 
constructor TApplicationPage.Create ( TheOwner: TComponent ) ;
begin
  inherited Create ( TheOwner ) ;
  fRecord := PEmptyAppRecord; // PEmptyAppRecord ist ein leerer Record (eine konstante)
  { ab hier werden dann die anderen steuerelemente erstellt }
  { also ein label }
  LabelId := TLabel.Create(self);
  with LabelId do begin
    Caption := 'ID:';
    SetBounds(20,20,Width,Height);
    Parent := self;
  end;
  { und ein edit-feld }
  EditId := TEdit.Create(self);
  with EditId do begin
    EditId.Caption := '';
    SetBounds(80,20,Width,Height);
    Enabled := false;
    Parent := self;
  end;
end;
 
constructor TApplicationPage.Create ( TheOwner: TComponent;
  PRecord: PDBApplicationRecord ) ;
begin
  Create(TheOwner);
  SetRecordP(PRecord);
end;
 
function TApplicationPage.GetId: LongInt;
begin
  result := fRecord^.ID;
end;
Die konkreten Auswirkungen sehen so aus:
1. Die erste Seite wird erstellt, ist aber leer
2. Die zweite Seite wird auch erstellt, bei einem Klick auf den entsprechenden Eintrag im Notebook wird sie angezeigt, enthält aber nur ein Editfeld (mit falscher ID)
3. Bei einem Wechsel zurück zur ersten Seite sind hier jetzt Label und Edit-Feld vorhanden
4. Wird eine Weitere Seite erstellt, wird die aktuelle Seite leer (sie wird jedoch bei einem hin und her wechseln wieder gefüllt)

Die Reihenfolge, in der die einzelnen Seiten erstellt werden, scheint keine Rolle zu spielen (jedoch habe ich noch keine ausführliche Testreihe gemacht, da dies keine Rolle spielen sollte).

Vielleicht weiß jemand von euch, was ich falsch mache...
Mfg die Socke

Benutzeravatar
theo
Beiträge: 10871
Registriert: Mo 11. Sep 2006, 19:01

Beitrag von theo »

http://bugs.freepascal.org/view.php?id=8346" onclick="window.open(this.href);return false;

Christian
Beiträge: 6079
Registriert: Do 21. Sep 2006, 07:51
OS, Lazarus, FPC: iWinux (L 1.x.xy FPC 2.y.z)
CPU-Target: AVR,ARM,x86(-64)
Wohnort: Dessau
Kontaktdaten:

Beitrag von Christian »

Der Bug den Theo verlinkt hat, ist soweit ich das Problem kenne nur im Designer vorhanden.
Dein Problem hört sich für mich eher nach einem Problem in deinem Code an. Ich mache im prometheus etwas ähnliches und da hab ich keinerlei probleme mit dem PageControl. Schau doch mal mit dem Debugger durch ob alle Komponenten auch erstellt werden.
W.m.k.A.h.e.m.F.h. -> http://www.gidf.de/

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Beitrag von Socke »

Danke erstmal für eure Antworten.
Der Bug den Theo verlinkt hat, ist soweit ich das Problem kenne nur im Designer vorhanden.
Laut Beschreibung auch aber auch nur unter GTK/GTK2. Ich benutzte jedoch Win32 zur Laufzeit.
Schau doch mal mit dem Debugger durch ob alle Komponenten auch erstellt werden.
Ich hab das Erstellen der einzelnen Pages mal mit dem Debugger Schritt für Schritt überprüft. Wenn die Komponenten nicht erstellt würden, sollten doch eigentlich Exceptions ausgelöst werden, da ich ja sofort auf sie zugreife?! Bei abfrage der Objektvariablen im Debugger kam jedoch immer ein nicht nil wert heraus. Auch eine Abfrage, die mir alle vorhandenen Informationen zu den einzelnen Seiten ausgibt, ergab das gleicher Ergebnis: Objektvariablen nil usw. Das einzig interessante wäre evtl, dass mein TNotebook.PageCount doppelt so groß ist, wie es eigentlich sein sollte. D.h. bei einer Seite ist PageCount 2, bei zwei Seiten 4 usw... jeweils zwei einträge in Page[] liefern jedoch die selbe Seite zurück (gleicher Speicheradresse, usw).
Dein Problem hört sich für mich eher nach einem Problem in deinem Code an.
Darauf bin ich auch schon gekommen, nur ist anscheinend alles syntaktisch korrekt (der Compiler schluckt den Code) und ich sehe/finde keinen Fehler.

In der Hoffnung, dass hier kein Bug vorliegt....
MfG Die Socke

Edit:
Für diejenigen, die es interessiert:
Ich habe vorhin, mal meine eigene TPage-Klasse eigenständig getestet, d.h. in ein neues Projekt eingebunden... und tada.... es funktioniert... und warum, weil ich vergessen hatte, page.Parent auf mein Notebook zu setzten. Also hab ich diese Nachlässigkeit in meinem Projekt ausprobiert und auch hier hat's funktioniert. Daraus resultiert zum hinzufügen von TPage-Abkömmlingen zur Laufzeit:

Code: Alles auswählen

var page: TMyPage;
     Notebook: TNotebook;
begin
  page := TMyPage.Create(self);
  [B]// page.Parent muss nil sein[/B]
  Notebook.PageList.Add(page);
end;
Dann funktioniert alles einwandfrei....

Antworten