[Gelöst] Parent casten

Rund um die LCL und andere Komponenten
Antworten
ConcAPPtLab
Beiträge: 89
Registriert: Fr 18. Apr 2014, 18:57

[Gelöst] Parent casten

Beitrag von ConcAPPtLab »

Hallo Community,

ich möchte eine Funktion schreiben, die alle Objekte mit einem gleichen Namenbestandteil zählt. Also z.B. die Anzahl der PaintBoxen mit 'PaintBox' im Namen ermittelt.

Ursprünglich sollte der Code so aussehen:

Code: Alles auswählen

function countObj(obj: string): integer;
var i: integer;
begin
 
  result:=0;
 
  for i:=0 to Application.ComponentCount-1 do
    begin
      if Pos(lowercase(obj), lowercase(Application.Components[i].Name))>0 then
      Inc(result);
    end;
 
end;


Aber schon bald habe ich gemerkt, dass in Application.Components nur Form1 und Form2 enthalten sind, nicht die Objekte, die ich zählen möchte.

Normalerweise hätte ich jetzt einfach Application durch Form1 ersetzt. Aber die gesuchten Objekte befinden sich auf einem Panel, sind also auch nicht in Form1.Components enthalten.

Um die Funktion allgemein zu halten, möchte ich Sie deswegen folgendermaßen deklarieren:

Code: Alles auswählen

function countObj(parent: TObject; obj: string): integer;
var i: integer;
begin
 
  result:=0;
 
  for i:=0 to parent.ComponentCount-1 do
    begin
      if Pos(lowercase(obj), lowercase(parent.Components[i].Name))>0 then
      Inc(result);
    end;
 
end;


Allerdings muss parent ja vor dem Zugriff noch gecastet werden. Trotzdem möchte ich ungerne jedes Mal eine extra Funktion für Formulare, Panel etc erstellen, bzw. eine lange Unterscheidung des Typs vornehmen.

Hier deswegen meine Frage: Gibt es eine Möglichkeit TObject so zu casten, dass man ein beliebiges Parentfähiges Objekt (Panel, Form...) als parent eingeben kann? Im Internet habe ich nur die festen Castings (as, TPanel()...) gefunden.

Grüße
ConcAPPtLab
Zuletzt geändert von ConcAPPtLab am Mi 25. Nov 2015, 12:37, insgesamt 3-mal geändert.
Definition "Strategische Fehlerkorrektur":
Solange rumprobieren bisses klappt :D

Warf
Beiträge: 1910
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Parent casten

Beitrag von Warf »

versuche es mal rekursiv:

Code: Alles auswählen

function CountComponentes(p: TComponent; prefix: String): Integer;
var i: Integer;
begin
  Result:=0;
  for i:=0 to p.ComponentCount-1 do
  begin
    if AnsiStartsText(prefix, p.Components[i].Name) then
      inc(result);
    Result := Result + CountComponents(p.Components[i], prefix);
  end;
end;
...
//Aufruf:
  c := CountComponents(Application, 'Button');
Zuletzt geändert von Warf am Di 24. Nov 2015, 18:00, insgesamt 1-mal geändert.

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

Re: Parent casten

Beitrag von wp_xyz »

Nimm für "parent" statt TObject diejenige Klasse, die als erste ComponentCount unterstützt, also TComponent - dann brauchst du auch keine Typumwandlungen:

Code: Alles auswählen

 function CountObj(Parent: TComponent; obj: string): integer;
var
  i: integer;
begin
  result:=0;
  for i:=0 to Parent.ComponentCount-1 do
    begin
      if Pos(lowercase(obj), lowercase(Parent.Components[i].Name))>0 then
      Inc(result);
    end;
end;

Oder meinst du evtl ControlCount? (ich vergesse immer wieder den Unterschied...). Dann wäre TWinControl die richtige Klasse.

ConcAPPtLab
Beiträge: 89
Registriert: Fr 18. Apr 2014, 18:57

Re: Parent casten

Beitrag von ConcAPPtLab »

EDIT: zu früh gefreut....Die Funktion läuft mit TComponent, allerdings gibt parent.ComponentCount nun 0 zurück, obwohl Objekte auf dem Parent liegen....ich öffne schon mal firefox

Hallo wp_xyz und Warf,

danke für eure extrem schnellen Antworten!

@Warf: was für ein Zufall, unsere Info-Lehrerin hat heute Funktionen/Prozeduren angefangen (war langweilig) und hat die ganze Stunde davon gefaselt, dass die besonders dann notwendig werden, wenn wir mit Rekursion anfangen werden. Haben sich alle gefragt, was das denn überhaupt ist. Und nun kommst du mit dem Vorschlag :D
Aber zu deiner Antwort: ich verstehe deinen Code noch nicht ganz. Durch ein wenig googeln könnte ich das aber bestimmt verstehen.

Doch da ich @wp_xyz's Code naürlich viel einfcher finde, habe ich das ausprobiert. Und es hat funktioniert :mrgreen:
Und nein, du hast es nicht vertauscht ^^

Gibt es denn einen Vorteil von der rekursiven variante gegenüber dem einfachen Austauschen von TObject mit TComponent?

Aber es läuft erstmal, vielen Dank an euch zwei.

Grüße
Definition "Strategische Fehlerkorrektur":
Solange rumprobieren bisses klappt :D

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

Re: [So halb gelöst] Parent casten

Beitrag von wp_xyz »

In der Hilfe zu TComponent.ComponentCount steht: "ComponentCount returns the number of components that the current component owns. It can be used to determine the valid index range in the Component array". Wichtig ist das Wort "owns": Der "Owner" einer Komponente ist dafür verantwortlich, die Komponente wieder aufzuräumen, wenn sie nicht mehr gebraucht wird. Der Owner wird in der Regel (immer?) beim Erzeugen angegeben. Bei den Standardkomponenten ist das fast immer das Formular, in dem sich die Komponente befindet.

Zu TWinControl.ControlCount findet man: "The number of immediate child controls". Hier liegt die Betonung auf "immediate". Das heißt, ControlCount liefert die Controls, die sich direkt in einem "Container"-Control befinden.

Jetzt ein Beispiel:
Nimm ein Formular (Form1), auf dem sich ein Edit und ein Panel (Panel1) befinden. In dem Panel sind zwei weitere Edits. Aufgabe ist es, mit ComponentCount bzw. ControlCount die Edits zu zählen, und zwar durch Aufrufe von Form1.ComponentCount bzw .ControlCount und von Form1.Panel1.ComponentCount / .ControlCount.

(1) ComponentCount, vom Formular aus aufgerufen, findet den Wert 3, weil alle drei Edits den Formular "gehören", also von ihm zerstört werden dürfen,
(2) ComponentCount. vom Panel aus aufgerufen, ergibt 0, weil das Panel die sich darauf befindenden Edits nicht zerstören darf, das macht ja das Formular
(3) ControlCount vom Formular aus aufgerufen, ergibt 1 - es ist nur 1 edit direkt auf dem Formular, die beiden anderen stecken in dem Panel
(4) ControlCount vom Panel aus aufgerufen, ergibt 2 - es enthält nur diese beiden Edits.

Nur zur Rekursion:
Wenn in dem Panel sich ein weiteres Panel (Panel2) mit einem Edit befindet und du musst alle Edits zählen, die sich direkt im 1.Panel (Panel1) und indirekt im 2.Panel befinden, musst du zuerst die Controls auf Panel1 durchlaufen: Wenn du ein Edit findest, wird es gezählt, wenn du ein Panel findest, musst du dieses Panel in genau derselben Weise durchlaufen wie Panel1. Genau das ist ein rekursiver Prozess.

Wir brauchen eine Prozedur - nennen wir sie CountEdit -, die auf ein beliebiges TWinControl angewendet werden kann; das WinControl wird als Parameter übergeben. In dieser Prozedur machen wir genau das, was oben beschrieben ist. Inbesondere ruft diese Prozedur sich mit jedem gefundenen Panel immer wieder selbst auf. Dass das ganze ein Ende findet, funktioniert nur, weil es irgendwann Controls gibt, die keine weiteren Controls mehr enthalten. Und ich sollte natürlich noch erwähnen, dass das ganze nicht nur für Panels funktioniert, sondern für alle TWinControls:

Code: Alles auswählen

 
procedure CountEdit(AParent: TWinControl; var Count: Integer);
var
  i: Integer;
begin
  // 1.fall - AParent ist selbst ein Edit (zur Vereinfachung soll der Name mit "Edit" beginnen")
  if Pos('Edit', AParent.Name) = 1 then
    inc(Count)
  else
  // 2.fall - alle "Kinder" von AParent durchlaufen und für jedes wieder "CountEdit" aufrufen
  for i:=0 to AParent.ControlCount-1 do
    CountEdit(AParent.Controls[i], Count);
end;
 


Diese Prozedur rufst du für das Control auf, das du untersuchen willst, also Form1.Panel1. Wichtig noch: nicht vergessen den Zähler auf null zu setzen

Code: Alles auswählen

 
begin
  Count := 0;
  CountEdit(Form1.Panel1, Count);
end;


Ich gebe dir recht, Rekursion ist am Anfang etwas schwer zu verstehen, aber sie ist in meinen Augen ein extrem eleganter Algorithmus. Ohne sie wären "beliebig" große Baumstrukturen wie eine Orderstruktur auf einer Festplatte nicht zu handhaben.
Dateianhänge
Form-Panel-Edit.png
Form-Panel-Edit.png (25.67 KiB) 1374 mal betrachtet

TBug
Beiträge: 177
Registriert: Mi 2. Sep 2015, 11:09
OS, Lazarus, FPC: Lazaurus 2.2.4 FPC 3.2.2
CPU-Target: Windows 32/64bit

Re: [So halb gelöst] Parent casten

Beitrag von TBug »

ConcAPPtLab hat geschrieben:ich möchte eine Funktion schreiben, die alle Objekte mit einem gleichen Namenbestandteil zählt. Also z.B. die Anzahl der PaintBoxen mit 'PaintBox' im Namen ermittelt.


Ist es wirklich das Ziel, nur die PaintBoxen zu ermitteln, welche auch PaintBox im Namen tragen, oder doch eher alle Objekte des gleichen Typs, also TPaintBox, egal ob sie PaintBox1, PaintBox2 oder aber PB_Picture heißt?


.

ConcAPPtLab
Beiträge: 89
Registriert: Fr 18. Apr 2014, 18:57

Re: [So halb gelöst] Parent casten

Beitrag von ConcAPPtLab »

Mannomann, da hast du aber ganz schön viel getippt, wp_xyz. Und das nur für mich! Aber es hat sich gelohnt, ich habe jetzt den Gedanken hinter Rekursion verstanden. Vorher habe ich angefangen, mich durch ein Dephi Treff Tutorial zu arbeiten....da war das viel komplizierter....

An TBug: Ja, wirklich nur die Namen durchsuchen, nicht die Typen.

Ich habe es jetzt doch noch anders gelöst (weil auch eleganter, meiner Meinung anch), indem ich meine PaintBoxen als neue Klasse definiert habe. So muss ich nun nicht mehr zählen, wie viele vorhanden sind, sondern merke mir, wie viele iche rstellt habe. Für das weitere Programm ist eine eigene Klasse sowieso besser geeignet. Aber trotzdem nochmal vielen lieben Dank an euch beide, sollte das Problem in Zukunft nochmal auftreten, weiß ich, wie es zu lösen ist.

LG
ConcAPPtLab
Definition "Strategische Fehlerkorrektur":
Solange rumprobieren bisses klappt :D

Antworten