![]() |
Delphi-Version: 7
Zur Laufzeit Comboboxen hinzufügen
Hallo,
ich würde gerne zur Laufzeit meines Programms per Buttonclick Comboboxen hinzufügen, dabei soll bei jedem weiteren Click eine neue Combobox unter der vorherigen angehängt werden. Wie kann ich das am besten realisieren? |
AW: Zur Laufzeit Comboboxen hinzufügen
Es gibt zwei Möglichkeiten.
1. Wenn, wie bei deinem anderen Thread, die Anzahl der möglichen Comboboxen limitiert ist, kannst du sie wie da schon vorher im Designermodus auf die Form legen und dann nach und nach sichtbar schalten. 2. Ist dies nicht der Fall, so musst du mit dynamischen Komponenten arbeiten, d.h. du kreierst die Comboboxen zur Laufzeit selbst. Dies sähe z.B. so aus:
Code:
Im Prinzip kreierst du die ComboBox als Variable und modifizierst sie so wie du sie brauchst, also Items einfügen, Größe und Position festlegen, Ereignisse verknüpfen etc.
Procedure KreiereComboBox;
Var ACombobox : TComboBox; Begin AComboBox := TComboBox.Create(GroupBox1); AComboBox.Parent := GroupBox1; AComboBox.Height := 14; AComboBox.Width := 50; AComboBox.Left := 5; AComboBox.Top := 10; AComboBox.Items.Add('Bla'); AComboBox.Items.Add('Bla'); AComboBox.Items.Add('Bla'); AComboBox.ItemIndex := 0; AComboBox.Name := 'XYZComboBox'; AComboBox.OnChange := BlaBlaBlubChange; End; Willst du später auf diese ComboBoxen nochmal zugreifen, ist es am sinnvollsten, diese dynamisch erzeugten Comboboxen z.B. in einer Liste abzuspeichern, um einen einfachen Zugriff zu haben, da diese im Gegensatz zum im Designer erzeugten Komponenten nicht direkt ansprechbar sind. |
AW: Zur Laufzeit Comboboxen hinzufügen
Man sollte aber noch den Parent setzen, damit sie auch gezeichnet wird und den Owner, damit der Owner das Zerstören übernimmt.
|
AW: Zur Laufzeit Comboboxen hinzufügen
Hier ein konstrukt wie man evtl an solch eine sache rangehen könnte.
Nur im Notepad geschrieben, also ungetestet!
Delphi-Quellcode:
Ich hoffe es hilft, im close event halt die cbList durchiterieren und .Free ranpappen.
var
Form1: TForm1; cbList: TList; implementation // wenn als click-event, dann sollte hier nur die cbList initialisiert werden und im click dann einfach adden procedure TForm1.FormCreate(Sender: TObject); var i: integer cb: TComboBox; begin cbList := TList.Create; for i := 0 to 9 do begin cb := TComboBox.Create(self); cb.Parent := self; cb.Top := (i * (cb.Height + 3)); // weitere optionen setzen cbList.Add(cb); end; // als beispiel alle boxen mit text versehen: for i := 0 to cbList.Count-1 do TComboBox(cbList[i]).Text := Format('ComboBox %d', [i]); end; |
AW: Zur Laufzeit Comboboxen hinzufügen
Der trauchige Witz ist, dass in der VCL sich auch der Parent um die Freigabe kümmert. (vermutlich auch im FMX der selbe Mist, aber hab mich nicht getraut nachzusehn)
|
AW: Zur Laufzeit Comboboxen hinzufügen
Ach genau, Parent vergessen. Habs angepasst.
Ja, in FMX isses genauso, muss man auch Parent und Owner festlegen. Mit dynamischen Komponenten zu arbeiten macht unglaublich viel Spaß. Deswegen greife ich für gewöhnlich auf TFrames zurück, wenn es nicht nur um einzelne Komponenten geht. Das macht die Sache deutlich angenehmer. |
AW: Zur Laufzeit Comboboxen hinzufügen
Zitat:
Delphi-Quellcode:
var
Form1: TForm1; cbList: TList; implementation procedure TForm1.FormCreate(Sender: TObject); begin cbList := TList.Create; end; procedure TForm1.Button1Click(Sender: TObject); var cb: TComboBox; begin cb := TComboBox.Create(self); cb.Parent := self; cb.Top := (cbList.Count * (cb.Height + 3)); // weitere eigenschaften setzen cbList.Add(cb); end; procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); var i: integer; begin // einfach mal testen ob's nötig ist, ich möchte mich nicht festlegen for i := 0 to cbList.Count-1 do TComboBox(cbList[i]).Free; cbList.Free; end; |
AW: Zur Laufzeit Comboboxen hinzufügen
Zitat:
Super! Das funktioniert einwandfrei. Vielen Dank für euere Hilfe! Nun abschließend noch; den Inhalt von Comboboxen fülle ich ja normalerweise in der StringList der Combobox in den Properties. Das funktioniert hier wie es scheint nicht, da ja noch keine Combobox vorhanden ist, welche ich füllen kann. Wie kann ich nun den Inhalt aller erzeugter Comboboxen vorbestimmen? |
AW: Zur Laufzeit Comboboxen hinzufügen
In dem du den Wert zur Laufzeit hinzufügst bzw. setzt. (ComboBox.Items)
|
AW: Zur Laufzeit Comboboxen hinzufügen
Zitat:
|
AW: Zur Laufzeit Comboboxen hinzufügen
Moin...:P
...nur zur Sicherheit: Delphi-Version: 7 :gruebel: Hast du die Möglichkeit auf die aktuelle Community Edition zu wechseln? Ich tippe darauf, daß du mit dem Lernen noch am Anfang stehst. :wink: |
AW: Zur Laufzeit Comboboxen hinzufügen
Seit wann ist der Parent für die Freigbae zuständig?! Das wird über den Owner gesteuert und nicht über den Parent. Wenn der Parent freigegeben wird, ist das enhaltene Objekt nach wie vor vorhanden und kann einfach an einen anderen Parent angehängt werden. Würde das durch den Parent gesteuert, würde viel eigener Code nicht mehr laufen, da ich in der Regel solche Sachen selber im Constructor erstelle und ich da keinen Owner im Create angebe. So hat man die volle Kontrolle über den Lebenszyklus einer eigenen Komponente und kann diese im Destructor der Komponente, innerhalb derer man das Object ertstellt hat, wieder freigeben. Mit dem Parent den man setzt hat das eigentlich nichts zu tun.
|
AW: Zur Laufzeit Comboboxen hinzufügen
Aus der Vcl.Controls.pas (Destruktor von TWinControl):
Zitat:
|
AW: Zur Laufzeit Comboboxen hinzufügen
Zitat:
Der Schuldige:
Delphi-Quellcode:
.Controls für Parent und .Components bei Owner
destructor TWinControl.Destroy;
begin ... I := ControlCount; while I <> 0 do begin Instance := Controls[I - 1]; Remove(Instance); Instance.Destroy; <<<<< I := ControlCount; end; ... end; Logisch ist es ein reiner Fail, aber ich vermute es ist als Kackfix gedacht, für zur Laufzeit erstellte Komponenten, wo die Entwickler zu doof sind den Owner anzugeben. :freak: Ich hätte lieber das Speicherleck, als unverständlich verschwindende Komponenten. |
AW: Zur Laufzeit Comboboxen hinzufügen
TWinControl kapselt ein Control der Win32-API. Diese kennen keinen Owner, sondern nur eine Parent, der für beides zuständig ist.
|
AW: Zur Laufzeit Comboboxen hinzufügen
TWinControl ist aber nicht unbedingt an die Lebensdauer des eingebetten Controls gekoppelt.
Der Innere kann sogar zwischendurch mal verschwinden und wird neu erstellt. (drum halten die Delphi-Controls auch alle Daten doppelt) Stell mal bei einer Form das StayOnTop um und erlebe, wie das Fenster und sein Inhalt freigebeben und neu erstellt wird ... drum darf man sowas wie z.B. eigene Registrierungen für Drag&Drop nicht im Constructor machen, sondern besser in CreateWnd bzw. CreateWindowHandle. Ich sehe da keinen Grund, dieses Verhalten daher im Delphi nachbauen zu müssen, wo es dort ja schließlich den Owner gibt. |
AW: Zur Laufzeit Comboboxen hinzufügen
Zitat:
Das wird dort beides gesetz. Einmal per COnstructor und dann noch separat der Parent per Zuweisung. Das einzige was einen stören könnte wäre, dass der COde als Prozedur und nicht als Methode umgesetzt ist. |
AW: Zur Laufzeit Comboboxen hinzufügen
Das hat er nach meinem Hinweis nachträglich eingefügt.
Vergleiche: Timestamp seines Beitrages Timestamp meines Beitrages Timestamp seines Änderung Und er hat den Änderungsgrund sogar genannt. |
AW: Zur Laufzeit Comboboxen hinzufügen
Zitat:
Combo.Items.Add('erster Eintrag'); Combo.Items.Add('zweiter Eintrag'); Combo.ItemIndex := 1; // Vorselektion 2. Eintrag, falls gewünscht |
AW: Zur Laufzeit Comboboxen hinzufügen
Ein neuer Versuch mit ein paar Zugaben, schönes Wochenende und diesmal wurden meinerseits auch Mängel berücksichtigt.
Viel Spass beim rumtesten. Die .pas Datei:
Delphi-Quellcode:
die .dfm Datei:
unit Unit1;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls; type TForm1 = class(TForm) cbPnl: TPanel; mainPnl: TPanel; infoPnl: TPanel; addBtn: TButton; procedure addBtnClick(Sender: TObject); private public // manuell eingefügte events die ich später verwende procedure cbChange(Sender: TObject); procedure cbClick(Sender: TObject); end; var Form1: TForm1; cbList: TList; const minHeight = 120; implementation {$R *.dfm} procedure TForm1.addBtnClick(Sender: TObject); var cb : TComboBox; begin // hier wird eine neue combobox erstellt mit ein paar grundeinstellungen // in diesem fall erstelle ich die cb in ein panel cb := TComboBox.Create(cbPnl); cb.Parent := Self; cb.Left := 3; cb.Top := cbList.Count * cb.Height + 3; cb.Style := csDropDownList; // damit man auch was zu sehen/klicken hat, ein paar einträge erzeugen cb.Items.Add('CB #' + IntToStr(cbList.Count)); cb.Items.Add('Cool #' + IntToStr(cbList.Count)); cb.ItemIndex := 0; // hier weise ich der cb events zu, für weitere events einfach diesem beispiel folgen cb.OnChange := cbChange; cb.OnClick := cbClick; // verwaltung der cb's cbList.Add(cb); // prototyp einer dynamischen formular größe if ((cbList.Count + 2) * cb.Height + 3) > minHeight then Height := (cbList.Count + 2) * cb.Height + 3; end; procedure TForm1.cbChange(Sender: TObject); begin infoPnl.Caption := TComboBox(Sender).Text; end; procedure TForm1.cbClick(Sender: TObject); begin infoPnl.Caption := TComboBox(Sender).Text; end; initialization cbList := TList.Create; finalization cbList.Free; end.
Delphi-Quellcode:
object Form1: TForm1
Left = 192 Top = 125 BorderIcons = [biSystemMenu] BorderStyle = bsSingle Caption = 'DynCbDemo by KodeZwerg' ClientHeight = 81 ClientWidth = 315 Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'Tahoma' Font.Style = [] OldCreateOrder = False Position = poScreenCenter PixelsPerInch = 96 TextHeight = 13 object cbPnl: TPanel Left = 0 Top = 0 Width = 150 Height = 81 Align = alLeft TabOrder = 0 end object mainPnl: TPanel Left = 150 Top = 0 Width = 165 Height = 81 Align = alClient TabOrder = 1 object infoPnl: TPanel Left = 1 Top = 1 Width = 163 Height = 41 Align = alTop BevelOuter = bvNone Caption = 'CB Item Display' TabOrder = 0 end object addBtn: TButton Left = 16 Top = 48 Width = 129 Height = 25 Caption = 'Add ComboBox' TabOrder = 1 OnClick = addBtnClick end end end |
AW: Zur Laufzeit Comboboxen hinzufügen
Jetzt noch das Erstellen in eine separate Methode aufrufen, die von der Schaltfläche aufgerufen wird mit der Position usw. als Parameter und man kann den Code universell einsetzten.
|
AW: Zur Laufzeit Comboboxen hinzufügen
Dann aber bitte die Liste noch als Feld es Formulars deklarieren, in dessen FormCreate erzeugen und im FormDestroy wieder freigeben, sonst bekommt man ganz schnell Kummer, wenn das Formular dynamisch erzeugt und freigegeben wird.
|
AW: Zur Laufzeit Comboboxen hinzufügen
Das gehört eigentlich nicht mehr dazu. Vielleicht will ja jemand die Comboboxen anders verwalten. In einem statischen Array oder in einer Objektliste oder...Aber das ist der Basiscode, den man auf alle Fälle benötigt.
|
AW: Zur Laufzeit Comboboxen hinzufügen
Zitat:
Also
Delphi-Quellcode:
Self steht in diesem Fall für's aktuelle Formular, wenn man den Schnippsel als methode verfrachtet sollte "Self" mit 'nem Formular-Bezeichner ausgetauscht werden und als Parent halt ein Control oder Formular angeben.
cb := TComboBox.Create(Self);
cb.Parent := cbPnl; Joar, die cbList in Formular Klasse mit rein, das macht Sinn, mein Fehler! (keine Ironie) Ich hätte gerne noch 'ne mini Erklärung warum Liste.Create/.Free eher in FormCreate/Destroy rein sollte. initialization/finalization ist doch eigentlich 'ne super stelle dafür, finde ich. |
AW: Zur Laufzeit Comboboxen hinzufügen
Zitat:
Zitat:
|
AW: Zur Laufzeit Comboboxen hinzufügen
Wenn das Formular und die Liste unterschiedliche Lebenszyklen haben, dann hat man recht schnell tote Referenzen in der Liste. Sobald der Parent/Owner der ComboBoxen freigegeben wird, gibt dieser auch die ComboBoxen frei. Diese stehen aber weiterhin in der Liste. Da macht es doch eher Sinn, die Lebenszyklen der Liste und des Formulars anzugleichen, außerdem braucht man dann auch keine globale Variable mehr.
|
AW: Zur Laufzeit Comboboxen hinzufügen
Super erläuterung und klar Formular Feld an Lebensdauer des Formulars binden macht Sinn.
Danke Euch! |
AW: Zur Laufzeit Comboboxen hinzufügen
Eine TComponentList könnte man auch verwenden.
Wird eine Komponente (TComponent) freigegeben, dann wird sie autoamtisch aus der Liste entfernt. TComponent haben ein Notify-System, um sich gegenseitig über ihre Freigabe zu informieren, z.B. Property auf nil zu setzen oder sich aus den Listen der Components und Controls ihrer Parents zu entfernen. Und in den NextGen-Compilern (Android, iOS) gibt es Weak-Referenzen, die werden automatisch auf nil gesetzt, wenn man die Komponente freigibt. |
AW: Zur Laufzeit Comboboxen hinzufügen
Zitat:
ich würde mir eine separate unit erzeugen mit einer type dynCreate = record deklaration und darin funktionen wie "ComboBox" definieren, aber wie ich die verschiedensten möglichkeiten einer verwaltung implementiere, da bin ich ratlos. für den TE sollte das bereits vorhandene ausreichen denk ich mal. |
AW: Zur Laufzeit Comboboxen hinzufügen
Zitat:
|
AW: Zur Laufzeit Comboboxen hinzufügen
Jede Komponente hat FreeNotifications, die sie u.a. an diejenigen Komponenten versendet, die sie als Besitzer festgelegt haben.
Wenn eine Komponente zerstört wird, werden deren Subkomponenten, die in einer (eigentlich zwei) Komponentenlisten gespeichert sind, automatisch freigegeben. Nicht zu verwechseln ist dies mit den eingebetteten Steuerelementen, die in TList<TControl> gespeichert werden; diese haben nichts damit zu tun. |
AW: Zur Laufzeit Comboboxen hinzufügen
siehe TComponentList (unit Contnrs)
Eine TList, die ein Dummy-TComponent drinnen hat oder die selbst ein TComponent ist, über dessen TComponent.Notification beim opRemove es das jeweilige Objekt aus der Liste löscht. TList.Notify bei lnAdded via TComponent.FreeNotification registrieren und beim lnExtracted/lnDeleted via TComponent.RemoveFreeNotification deregistrieren. (wer bei wem ist egal, da alles gegenseitig)
Delphi-Quellcode:
Nochmal als Beispiel mit einem Property:
procedure TComponentListDummy.Notification(AComponent: TComponent; Operation: TOperation);
begin if Operation = opRemove then Parent.Extract(AComponent); // Item aus Liste entfernen, ohne enthaltenes Objekt (nochmal) freizugeben inherited; end; procedure TComponentList.Notify(Ptr: Pointer; Action: TListNotification); begin if Assinged(Ptr) then case Action of lnAdded: TComponent(Ptr).FreeNotification(FDummy); lnExtracted, lnDeleted: TComponent(Ptr).RemoveFreeNotification(FDummy); end; inherited; end;
Delphi-Quellcode:
type
TDemo = class(TComponent) private FLink: TComponent; procedure SetLink(Value: TComponent); protected procedure Notification(AComponent: TComponent; Operation: TOperation); override; public property Link: TComponent read FLink write SetLink; end; procedure TDemo.SetLink(Value: TComponent); begin if Assigned(FLink) then Self.RemoveFreeNotification(FLink); // oder FLink.RemoveFreeNotification(Self); FLink := Value; if Assigned(FLink) then Self.FreeNotification(FLink); end; procedure TDemo.Notification(AComponent: TComponent; Operation: TOperation); begin inherited; if Operation = opRemove then begin if FLink = AComponent then FLink := nil; end; end; |
AW: Zur Laufzeit Comboboxen hinzufügen
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:
Vielleicht auch für den TE oder andere interessant, aufbauend auf den ![]()
Delphi-Quellcode:
unit Unit1;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, Contnrs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; Button2: TButton; procedure Button1Click(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure Button2Click(Sender: TObject); private { Private-Deklarationen } ComponentList: TComponentList; public { Public-Deklarationen } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var cb: TComboBox; begin cb := TComboBox.Create(self); cb.Parent := self; cb.Top := (ComponentList.Count * (cb.Height + 3)); // weitere eigenschaften setzen cb.Items.Add(DateTimeToStr(now)); // mit irgendein Text blabla Text füllen cb.ItemIndex := 0; ComponentList.Add(cb); Caption := 'ComponentList: ' + IntToStr(ComponentList.Count); end; procedure TForm1.Button2Click(Sender: TObject); var cb: TComboBox; begin if ComponentList.Count > 0 then begin cb := TComboBox(ComponentList[ComponentList.Count-1]); cb.Free; Caption := 'ComponentList: ' + IntToStr(ComponentList.Count); end; end; procedure TForm1.FormCreate(Sender: TObject); begin ComponentList := TComponentList.Create; Caption := 'ComponentList: ' + IntToStr(ComponentList.Count); end; procedure TForm1.FormDestroy(Sender: TObject); begin ComponentList.Free; end; end. |
AW: Zur Laufzeit Comboboxen hinzufügen
Danke an himitsu und jus für den Component-Notify tipp/Beispiel-Code, ich werde übers Wochenende mal 'ne kleine DynamicControls unit erschaffen :-)
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 00:29 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz