Eigene Komponente mit mehreren Einträgen - wie im OI?

Rund um die LCL und andere Komponenten
Antworten
CCRDudeLaz
Beiträge: 54
Registriert: Do 25. Jan 2024, 08:33
OS, Lazarus, FPC: Win/macOS (L trunk FPC trunk)
CPU-Target: 32+64

Eigene Komponente mit mehreren Einträgen - wie im OI?

Beitrag von CCRDudeLaz »

Ich hab das auch im englischsprachigen Forum gefragt, aber vielleicht zu umständlich ausgedrückt, daher hier ein frischer Anfang :)

Ich habe diverse Komponenten geschrieben, die ich bisher zur Laufzeit erstellt und gefüllt haben. Nun will ich auf Designzeit umstellen. Die Komponenten an sich sind auch kein Problem. Was ich aber nicht hinbekomme:

Eine Komponente beispielsweise ist ein Menü. D.h. sie hat eine flexible Anzahl an Menüeinträgen, die zur Designzeit bearbeitet werden sollen. PropertyEditor und ComponentEditor kann ich prinzipiell erstellen, aber nicht korrekt verwenden.

Den PropertyEditor kann ich auf meine Property MenuItems setzen und dann nach Vorbild IDE einen modalen Dialog öffnen. Aber wenn ich beispielsweise Action zuweisen will, wäre es praktisch, das gewählte MenuItem im OI zu bearbeiten - wie bekomme ich das da hin?

Eigentlich sollte TMainMenu oder TPopupMenu ja ein gutes Vorbild sein - MenuItems sind als Untereinträge im OI, lassen sich dort wählen und bearbeiten. Nur wie bekomme ich sie da hin? Habe GetChildren und GetEnumerator implementiert, scheint aber nicht zu helfen? Kann für die MenuItem SetSubComponent setzen - hilft auch nichts. Was übersehe ich?

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6770
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Eigene Komponente mit mehreren Einträgen - wie im OI?

Beitrag von af0815 »

Ich glaube mich zu erinnern die müssen eine bestimmte Vererbung haben, damit der OI sie kennt. Ich habe im Zuge eines Bugreports mal den OI Standalone betrieben, damit man genau solche Sachen testen kann. Der alte Code ist hier https://github.com/afriess/LazarusBug00 ... ree/master

Damals habe ich wegen nicht unterstützter Editoren für Interfaces das ganze geschrieben. Vielleicht ist da eine Idee dabei.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

wennerer
Beiträge: 607
Registriert: Di 19. Mai 2015, 20:05
OS, Lazarus, FPC: Linux Mint 20 Cinnamon,Lazarus 2.2.6 (rev lazarus_2_2_6) FPC 3.2.2 x86_64-linux-
CPU-Target: x86_64-linux-gtk2

Re: Eigene Komponente mit mehreren Einträgen - wie im OI?

Beitrag von wennerer »

Hallo CCRDudeLaz,
ich bin mir nicht sicher ob es das ist was du suchst aber ich habe mir mal zu Lernzwecken eine kleine Komponente geschrieben in der man Bälle zur Designzeit als Item anlegen kann.
Ich hänge dir die Komponente einfach mal an. In dem Anhang findest du auch ein Verzeichnis Test dort ist eine kleine Demo-Anwendung. Die Komponente wird in einer Lasche "Meine" erzeugt.

Um zur Designzeit Bälle hinzuzufügen einfach hinter Circles auf die drei Punkte klicken. Vielleicht hilft es dir ja weiter.


Circles.png
Circles.png (61.93 KiB) 1254 mal betrachtet

Viele Grüße
Bernd
Dateianhänge
MinimalCollection.zip
(115.21 KiB) 37-mal heruntergeladen

CCRDudeLaz
Beiträge: 54
Registriert: Do 25. Jan 2024, 08:33
OS, Lazarus, FPC: Win/macOS (L trunk FPC trunk)
CPU-Target: 32+64

Re: Eigene Komponente mit mehreren Einträgen - wie im OI?

Beitrag von CCRDudeLaz »

Herzlichen Dank für Eure Antworten!

Wenn ich mir das so ansehe, habe ich "viel zu viel" gemacht. Meine Items sind bisher in einer TObjectList<meineitemtyp>, ich dachte wenn ich den Enumerator zur Verfügung stelle und das GetChildren &c, müsste das ja gehen.

Ich habe dann jetzt mal auf TCollection abgeändert, und es funktioniert einfach so, direkt. :oops: :D
Kleiner Nachteil vielleicht: so haben die kein Name-Property, und ich muss mir überlegen, wie ich die zur Laufzeit anspreche - da ich sie aber mit Actions verknüpfe, müsste alles notwendige (Visible/Enabled) ja über die Actions gehen.

Einen "eigenen" OI - erst das TTIPropertyGrid, dann TObjectInspectorDlg - hatte ich auch, quasi als Hilfsmittel, solange meine Controls noch nur zur Laufzeit gingen. Der zeigt mein MenuItems immer noch nicht im Baum an, aber über den Dialog hinter den drei Punkten geht's auch da.

Großartig!

Zwei Controls habe ich jetzt funktionierend, als Nächstes mache ich mich an eine Tabelle, die hat Hierarchien, bin sehr gespannt wie das da klappt - da ich da aber keine Actions brauche, kann ich im Zweifel einfach auf einen Property Editor ausweichen :)

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6770
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Eigene Komponente mit mehreren Einträgen - wie im OI?

Beitrag von af0815 »

CCRDudeLaz hat geschrieben: Di 16. Jul 2024, 09:50 Kleiner Nachteil vielleicht: so haben die kein Name-Property, und ich muss mir überlegen, wie ich die zur Laufzeit anspreche - da ich sie aber mit Actions verknüpfe, müsste alles notwendige (Visible/Enabled) ja über die Actions gehen.
Das sollte doch beim Erstellen der Objekte gleich mit erledigt werden können. Der Namen der Komponente sollte sowieso eindeutig sein, damit findet man das leichter.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

CCRDudeLaz
Beiträge: 54
Registriert: Do 25. Jan 2024, 08:33
OS, Lazarus, FPC: Win/macOS (L trunk FPC trunk)
CPU-Target: 32+64

Re: Eigene Komponente mit mehreren Einträgen - wie im OI?

Beitrag von CCRDudeLaz »

Bin mir nicht sicher, ob ich Dir folgen kann :)

Als ich eine eigene TObjectList<> verwendet habe, waren meine Menüeinträge abgeleitet von TComponent (abgeschaut von TMenuItem), jetzt von TCollectionItem haben sie das eben nicht mehr.

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6770
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Eigene Komponente mit mehreren Einträgen - wie im OI?

Beitrag von af0815 »

https://www.freepascal.org/docs-html/rt ... nitem.html
property DisplayName: string; [rw] Name of the item, displayed in the object inspector.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

wp_xyz
Beiträge: 5142
Registriert: Fr 8. Apr 2011, 09:01

Re: Eigene Komponente mit mehreren Einträgen - wie im OI?

Beitrag von wp_xyz »

af0815 hat geschrieben: Di 16. Jul 2024, 10:38 so haben die kein Name-Property, und ich muss mir überlegen, wie ich die zur Laufzeit anspreche
Wie bei allen Collections und ihren CollectionItems: Über den Index in der Collection

CCRDudeLaz
Beiträge: 54
Registriert: Do 25. Jan 2024, 08:33
OS, Lazarus, FPC: Win/macOS (L trunk FPC trunk)
CPU-Target: 32+64

Re: Eigene Komponente mit mehreren Einträgen - wie im OI?

Beitrag von CCRDudeLaz »

@wp_xyz: Klar gibt's den Index. Aber ich habe beispielsweise zwei verschiedene Builds mit leicht unterschiedlichem Menü, ich habe Standard-Einträge, und Einträge, die je nach Programm unterschiedlich sind - mir jetzt den passenden Index in einer Extra-Variable zu merken ist irgendwie nicht stilvoll.

@af0815: das entscheidende Stichwort war Laufzeit. Im OI ist mir das egal (da habe ich mir GetDisplayName mit der Caption überschrieben), ich meine tatsächlich den "Name"-Property, um zur Laufzeit etwas zu verändern.

Ich denke, das klappt alles trotzdem problemlos, weil meine Menü-Einträge ja über einen ActionLink mit einer Action verknüpft sind, und ich über diese ja zur Laufzeit indirekt zugreifen kann. Lieber wäre mir aber halt, wenn die Menüeinträge TComponent wären und ich im Code

Code: Alles auswählen

MenuEntry1.SubItem := PopupMenu1
sagen könnte ;)

Wenn ich mein Tabellen-Control umstelle, wird das wichtiger, weil die erste Hierarchie-Ebene Kategorien beinhaltet, in die ich später einsortiere, und dazu eine namentliche Referenz schon praktisch finde. Da muss ich dann rausfinden, was TMenuItem o.ä. anders und doch richtiger als ich machen.

wp_xyz
Beiträge: 5142
Registriert: Fr 8. Apr 2011, 09:01

Re: Eigene Komponente mit mehreren Einträgen - wie im OI?

Beitrag von wp_xyz »

Wenn die CollectionItems wirklich einen "Name" brauchen, dann erweitere ihre Deklaration um ein entsprechendes Feld. Und spendiere der Collection eine public-Methode "FindItemByName(AName: String)"

CCRDudeLaz
Beiträge: 54
Registriert: Do 25. Jan 2024, 08:33
OS, Lazarus, FPC: Win/macOS (L trunk FPC trunk)
CPU-Target: 32+64

Re: Eigene Komponente mit mehreren Einträgen - wie im OI?

Beitrag von CCRDudeLaz »

Aber der Vorteil des Property Name ist doch gerade, dass ich schon zum Zeitpunkt des Kompilierens Fehler angezeigt bekomme, wenn ich mich vertippe (um es mal ganz simpel zu sagen). FindItemByName verkompliziert mit Fehlerbehandlung zur Laufzeit unnötig :)

Wie schon erwähnt: TMenuItem kann das auch mit TLCLComponent.

Das soll ich keinster Weise schmälern, dass ich für die Hilfe hier sehr dankbar bin und mit TCollection in zwei Fällen gut weiterkomme.

Für die anderen habe ich damit quasi einen Workaround (ob jetzt Index speichern oder eine ID vom Typ String zum Suchen wie oben vorgeschlagen) - da man Tabellen aber eher zur Laufzeit befüllt, bin ich halt noch mit Ehrgeiz erfüllt, herauszufinden, wie das korrekt auch damit geht. Und ich werde natürlich den Thread hier auf dem Laufenden halten, wenn ich etwas herausfinde.

wennerer
Beiträge: 607
Registriert: Di 19. Mai 2015, 20:05
OS, Lazarus, FPC: Linux Mint 20 Cinnamon,Lazarus 2.2.6 (rev lazarus_2_2_6) FPC 3.2.2 x86_64-linux-
CPU-Target: x86_64-linux-gtk2

Re: Eigene Komponente mit mehreren Einträgen - wie im OI?

Beitrag von wennerer »

Hallo CCRDudeLaz,
vielleicht möchtest du auch die Eigenschaft DisplayName nutzen. In meinem minimal Beispiel ist dies nicht implementiert.
Du musst beim Item noch eine Property im Published Bereich hinzufügen:

Code: Alles auswählen

TCircle = class(TCollectionItem)
   private
    FCircles     : TCollection;
    FColor: TColor;
    FDiameter: integer;
    FEnabled: boolean;
    FVisible     : Boolean;
    FDisplayName : string;
    FLeft        : integer;
    FTop         : integer;

    procedure SetEnabled(AValue: boolean);
    procedure SetVisible(AValue: Boolean);
   protected
    function GetDisplayName: string; override;
    procedure SetDisplayName(const Value: string); override;
    function GetOwner: TPersistent; override;
   public
    constructor Create(ACollection: TCollection); override;
   published
    property Color    : TColor read FColor write FColor;
    property Diameter : integer read FDiameter write FDiameter;
    property Enabled  : boolean read FEnabled write SetEnabled default true;
    property Visible  : Boolean read FVisible write SetVisible default true;
    property Left     : integer read FLeft write FLeft;
    property Top       : integer read FTop write FTop;

    property DisplayName : string read GetDisplayName write SetDisplayName;//Diese Zeile einfügen
  end;                       
Dann die Funktion GetDisplayName so ändern:

Code: Alles auswählen

function TCircle.GetDisplayName: string;
begin
  //Result:=inherited GetDisplayName;
  Result := FDisplayName;
end;  
Nun kannst du in der Create einen Namen nach Wunsch vergeben. Natürlich kannst du auch zur Designzeit den Namen im OI ändern.

Viele Grüße
Bernd

CCRDudeLaz
Beiträge: 54
Registriert: Do 25. Jan 2024, 08:33
OS, Lazarus, FPC: Win/macOS (L trunk FPC trunk)
CPU-Target: 32+64

Re: Eigene Komponente mit mehreren Einträgen - wie im OI?

Beitrag von CCRDudeLaz »

Hallo Bernd,

Das ist sehr nett von Dir, aber es geht mir wirklich um eine Variable für die Einträge zur Laufzeit (habe ich mit "Property Name" wohl schlecht beschrieben), nicht um die Anzeige zur Designzeit.
Mal als Beispiel:

Code: Alles auswählen

var 
   cat: TMyTableDataCategory;
begin
   cat := myTable.TableData.AddCategory;
   cat.AddEntry('eins');
   cat.AddEntry('zwei');
   cat.AddEntry('drei');
...
Das funktioniert aber nur, wenn TMyTableDataCategory von TComponent, nicht von TCollectionItem, abgeleitet ist. In der LCL bei TMenuItem etwa funktioniert das ja auch, also muss es einen Weg geben :)

Klar, ich kann per Index darauf zugreifen, das wäre dann:

Code: Alles auswählen

var 
   iCatIndex: integer;
begin
   iCatIndex := myTable.TableData.AddCategory;
   myTable.TableData[iCatIndex].AddEntry('eins');
   myTable.TableData[iCatIndex].AddEntry('zwei');
   myTable.TableData[iCatIndex].AddEntry('drei');
...
Oder über eine ID (die ich für Exporte nach JSON/XML eh schon verwende):

Code: Alles auswählen

var 
   iCatIndex: integer;
begin
   myTable.TableData.AddCategory({categoryid} 'halloWelt');
   iCatIndex := myTable.TableData.FindData('halloWelt');
   if (iCatIndex > -1) then begin
      myTable.TableData[iCatIndex].AddEntry('eins');
      myTable.TableData[iCatIndex].AddEntry('zwei');
      myTable.TableData[iCatIndex].AddEntry('drei');
   end else begin
        // "unnötige" Fehlerbehandlung
   end;
...
Schon dieses einfache Beispiel zeigt auf dem Schirm die bessere Lesbarkeit von Code 1.

Außerdem läuft Variante 1 halt seit Jahren mit zur Laufzeit erstellten Controls, es sträubt sich mir ein wenig, da zurückzubauen, um zur Designzeit mehr Komfort zu haben ;)

Muss leider bis morgen warten, bis ich wieder per VPN auf meine Lazarus-VM komme, dann werde ich mich nochmal genau durch TMenuItem wühlen - und dank dem Tipp TCollection kann ich mich dabei auf exportierte Properties beschränken, die auch TCollection (oder sein PropertyEditor) besitzt, das ist sehr viel wert :)

CCRDudeLaz
Beiträge: 54
Registriert: Do 25. Jan 2024, 08:33
OS, Lazarus, FPC: Win/macOS (L trunk FPC trunk)
CPU-Target: 32+64

Re: Eigene Komponente mit mehreren Einträgen - wie im OI?

Beitrag von CCRDudeLaz »

Ist nicht so einfach, aber ich komme dem ganzen näher :)

gitlab repo.

Abgesehen von dem "kleinen" Glitch, dass beim Laden die Untereinträge in die Form statt das Test-Label geladen werden - da muss ich vielleicht noch an ReadState oder SetParentComponent arbeiten, sind das nun auch reguläre TComponent-Abkömmlinge, die als Untereinträge funktionieren :)

Antworten