Entwickler-Ecke
Grafische Benutzeroberflächen (VCL & FireMonkey) - ListBox: waagerechte Scrollbars
Chris1308 - Do 11.07.02 20:42
Titel: ListBox: waagerechte Scrollbars
Hi @all!
Wie kann ich einer Listbox eine waagerechte Scrollbar zuweisen?
Chris1308
DaDoc - Fr 12.07.02 08:26
z.B. mit
Quelltext
1:
| SendMessage(Listbox.Handle, LB_SetHorizontalExtent, BreiteInPixel, LongInt(0)); |
Sobald die Listbox kleiner als "BreiteInPixel" ist, wird eine horizontale ScrollBar angezeigt, der so ausgelegt ist, daß man damit den Bereich von "BreiteInPixel" überschauen kann. Dazu muß man aber natürlich erstmal wissen, wie breit der "breiteste" Eintrag in Pixel ist (mit aktuellen Font) ... :wink:
Sven
Chatfix - Mo 15.07.02 15:06
man sollte vieleicht doch die such-funktion des forums nutzen.. ichhatte das gleiche problem..
die lösung findest du hier:
http://www.delphi-forum.de/viewtopic.php?t=207
EDIT: Link aktualisiert, war noch auq.de
Prof. Dachs - Do 21.04.05 13:51
Hallo,
ich habe gerade die Funktion SendMessage ausprobiert. Das hat aber nicht funktioniert, weil LB_SetHorizontal ein "undeclared identifier" ist. Ich habe es auch mit Listbox1.Perform ausprobiert, selber Fehler. Kann das daran liegen dass ich mit Delphi 7 Personal arbeite? Oder an was sonst?
Danke schon im Voraus,
Markus
DaDoc - Do 21.04.05 14:25
Die Deklaration von "LB_SETHORIZONTALEXTENT" befindet sich in der Datei "Messages".
Btw.: Schau mal nach, ob deine Listbox die Eigenschaft "ScrollWidth" im Objektinspektor hat. Damit läst sich auch eine horizontale ScrollBar einblenden, wenn die Breite der Einträge (in Pixel) größer als die Breite der Listbox ist.
Sven
Prof. Dachs - Do 21.04.05 14:38
Mit der Einbindung der Unit Messages hat SendMessage jetzt funktioniert. thx@DaDoc.
Mit Scrollwidth hab ich des erst nich so ganz gepeilt, aber jetzt(nachdem ichs nochmal ausprobiert hab), funktioniert des auch). Also thx nochmal.
DaDoc - Do 21.04.05 14:51
Laut QuellCode von TListBox wird beim Setzen von "ScrollWidth" wird auch nichts anderes gemacht, als ein "SendMessage(Handle, LB_SETHORIZONTALEXTENT, Value, 0)". Bin ich zuerst auch nicht darüber gestolpert... :D
Sven
Dibelius - Fr 03.04.09 21:03
*Uralt-Thread ausgrab*
ich hab nach
diesem Tutorial [
http://www.delphi-treff.de/tipps/komponenten/wiki/Listbox%20mit%20horizontaler%20Scrollbar] für eine Listbox eine horizontale Scrollbar eingefügt. Funktioniert auch alles wunderbar... bis auf die Tatsache, dass sich gleiches Prinzip nicht auf eine zweite Listbox anwenden lässt, die ebenfalls in Form1 ist. Ich hab es sowohl mit gleichen als auch mit unterschiedlichen Variablen für die Scrollbar-Breite versucht. Klappte bisher alles nicht.
Kann mir da jemand eienn heißen Tipp geben?
delphi10 - Fr 03.04.09 21:11
Dibelius hat folgendes geschrieben : |
*Uralt-Thread ausgrab*
Kann mir da jemand eienn heißen Tipp geben? |
Versuchs mal damit:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| Procedure TFormX.SetHorizontalScrollBar(lb : TListBox); var j, MaxWidth,tmp: integer; begin MaxWidth := 0; tmp := 0; for j := 0 to lb.Items.Count - 1 do if MaxWidth < lb.Canvas.TextWidth(lb.Items[j]) then begin MaxWidth := lb.Canvas.TextWidth(lb.Items[j]); If MaxWidth > tmp then tmp := MaxWidth; end; SendMessage(lb.Handle,LB_SETHORIZONTALEXTENT,MaxWidth+300,0); end; |
Gruß delphi10
Edit. Ach ja, es kommt auf die Übergabe der Variable TListBox (Zeile1) an. Alles andere ist eigentlich genau so wie schon oben geschrieben.
SetHorizontalScrollBar(ListBox1);
SetHorizontalScrollBar(ListBox2); und so weiter...
Dibelius - Sa 04.04.09 13:49
uh, danke erstmal.
Aber irgendwie hab ich mit der Prozedur mehr Probleme. Erstmal haut das auch wieder nur für eine Listbox hin, obwohl ich als Parameter natürlich die jeweils richtige Listbox übergebe. Zudem seh ich die Items in der Listbox nun gar nicht mehr erst und es kommt zu unschönen Darstellungsfehlern beim Scrollen. Ist nicht so das Wahre :/
/EDIT:
Ich hab die Prozedur aus dem Tut nochmal abgekapselt von der ListBox.OnDrawItem Methode
Delphi-Quelltext
1: 2: 3: 4: 5:
| private flbHorzScrollWidth, flbHorzScrollWidth2: Integer; procedure AutoSizeListBox(TargetBox: TListBox; Index: integer; Rect: TRect; ScrollWidth: integer); |
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20:
| procedure TForm1.AutoSizeListBox(TargetBox: TListBox; Index: integer; Rect: TRect; ScrollWidth: integer); var Len: integer; NewText: string; begin NewText := TargetBox.Items[Index];
with TargetBox.Canvas do begin FillRect(Rect); TextOut(Rect.Left + 1, Rect.Top, NewText); Len := TextWidth(NewText) + Rect.Left + 3; if Len > ScrollWidth then begin ScrollWidth := Len; TargetBox.Perform(LB_SETHORIZONTALEXTENT, ScrollWidth, 0 ); end; end; end; |
Und dann beiden Listboxes im OI unter Events > OnDrawItem das der zweiten zugewiesen.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| procedure TForm1.ListBox2DrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState); begin case (Control as TListBox).Tag of 1: AutoSizeListBox(ListBox1, Index, Rect, flbHorzScrollWidth); 2: AutoSizeListBox(ListBox2, Index, Rect, flbHorzScrollWidth2); end; end; |
Leider funktioniert auch das wieder nur für eine Listbox (die mit Tag = 2), unabhängig davon, ob ich den Parameter "flbHorzScrollWidth" nur für eine oder für beide Listboxes verwende. :(
Das Problem liegt wohl woanders. Und zwar hab ich gerade mal den Code für die erste Listbox angepasst und ein showMessage eingefügt (in beiden Listboxes ist bei Programmstart zurzeit schon was eingetragen, was die Breite jeweils überschreitet).
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState); begin case (Control as TListBox).Tag of 1: showMessage('Test'); 2: AutoSizeListBox(ListBox2, Index, Rect, flbHorzScrollWidth2); end; end; |
Die showMessage bleibt aus... bei der 2. klappt es und ich versteh einfach nicht, warum...
JayEff - Sa 04.04.09 16:17
:shock: OnDrawItem? Meinst du nicht dass das ein winziges bisschen zu häufig ist? :rofl: Ändere die Größe des Scrollbalkens doch beim Einfügen oder Löschen von Einträgen
einmalig ;)
Edit:
Delphi-Hilfe zu OnDrawItem: |
Mit einer Ereignisbehandlungsroutine für OnDrawItem können Sie die Einträge von Listenfeldern zeichnen, deren Eigenschaft Style den Wert lbOwnerDrawFixed, lbOwnerDrawVariable oder lbVirtualOwnerDraw hat. |
OnDrawItem tritt
nur auf, wenn du Listbox.Style auf eine der OwnerDraw-Optionen gesetzt hast.
Dibelius - Sa 04.04.09 17:41
Würde ich gern so machen, wenn ich wüsste, was ich als "Rect" und "Index" an die AutoSizeListBox-Prozedur übergeben müsste...
JayEff - Sa 04.04.09 18:00
Andere Frage: Hat deine Listbox eine Eigenschaft ScrollWidth? Das ist nämlich das gleiche, nur gekapselt in einer simplen Zuweisung (Meine unter Delphi7 hat sie.) Falls nicht, kannst du die markierte Stelle mit
TargetBox.Perform(LB_SETHORIZONTALEXTENT, ScrollWidth, 0 ); ersetzen.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16:
| procedure TForm1.AutoSizeListBox(TargetBox: TListBox); var Len, i: integer; NewText: string; begin Len := TargetBox.Canvas.TextWidth(TargetBox.Items[0]); for i := 0 to TargetBox.Items.Count - 1 do begin NewText := TargetBox.Items[i]; if TargetBox.Canvas.TextWidth(NewText) > Len then Len := TargetBox.Canvas.TextWidth(NewText); end;
TargetBox.ScrollWidth := Len + 10; end; |
Aufruf:
AutosizeListbox(Listbox1);
Problem: Etwas langsam. Spürt man aber nicht wirklich bei 2000 Einträgen, denke ich. Einfach immer dann aufrufen, wenn ein Item gelöscht oder hinzugefügt wird. Wenn man's schneller haben will, kann man zunächst TargetBox.Items in eine TStringList schreiben und mit dieser arbeiten, ich hab das mal ausprobiert, kann durchaus schneller gehen, dadurch.
Wegen "Nicht in onDrawItem aufrufen": Da hab ich den Code falsch betrachtet, der ist für onDrawItem gedacht, definitiv. :oops:
Wenn der
alte Code nicht funktioniert, schau mal ob du beide Listboxes auf Style = dsOwnerDraw[irgendwas] gestellt hast, wenn nicht,
wird das onDrawItem Ereignis garnicht ausgelöst
delphi10 - Sa 04.04.09 18:23
Dibelius hat folgendes geschrieben : |
Würde ich gern so machen, wenn ich wüsste, was ich als "Rect" und "Index" an die AutoSizeListBox-Prozedur übergeben müsste... |
Zitat: |
Die showMessage bleibt aus... bei der 2. klappt es und ich versteh einfach nicht, warum... |
Hat denn eine ListBox den Tag= 1?
Wenn Du wirklich im Inspector für die ListBox den Style auf lbOwnerDrawVariable gestellt hast, musst du gar nichts übergeben und auch nicht explizit aufrufen. Das geht mit der Ausgabe eines Items ganz automatisch. Ist denn das Ereignis "OnDrawItem" richtig gesetzt mit "ListBox2DrawItem"? Schau Dir mal
http://www.delphipraxis.net/topic137508_verschiedene+farben+im+listboxstring.html an.
Dibelius - Sa 04.04.09 18:39
JayEff hat folgendes geschrieben : |
Wenn der alte Code nicht funktioniert, schau mal ob du beide Listboxes auf Style = dsOwnerDraw[irgendwas] gestellt hast, wenn nicht, wird das onDrawItem Ereignis garnicht ausgelöst |
argh, ich hatte wohl Tomaten auf den Augen. Danke, genau das war es, warum onDrawItem bei der einen Box nicht aufgerufen wurde. Kleiner Fehler, große Wirkung ^^
achso, eine Sache noch (nur zum Verständnis):
Wo wird überhaupt der Parameter "ScrollWidth" initialisiert? (s. meinen Code oben)
Ich deklariere es da zwar unter private, aber dann vergleicht die Prozedur plötzlich den Wert mit "Len" und erst bei Erfolg wird ein Wert zugewiesen. Welchen Wert hat "ScrollWidth" vor dem Vergleich?
JayEff - Sa 04.04.09 18:56
Dibelius hat folgendes geschrieben : |
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20:
| procedure TForm1.AutoSizeListBox(TargetBox: TListBox; Index: integer; Rect: TRect; ScrollWidth: integer); var Len: integer; NewText: string; begin NewText := TargetBox.Items[Index];
with TargetBox.Canvas do begin FillRect(Rect); TextOut(Rect.Left + 1, Rect.Top, NewText); Len := TextWidth(NewText) + Rect.Left + 3; if Len > ScrollWidth then begin ScrollWidth := Len; TargetBox.Perform(LB_SETHORIZONTALEXTENT, ScrollWidth, 0 ); end; end; end; |
Delphi-Quelltext 1:
| AutoSizeListBox(ListBox1, Index, Rect, flbHorzScrollWidth); | |
Die markierten Stellen sollten das erklären (auch wenn ich mich etwas wundere, da ScrollWith kein var-Parameter ist): Die globale Variable flbHorzScrollWidth speichert die ScrollWidth der entsprechenden Listbox wie sie vor dem Aufruf der Autosize-Prozedur ist. Dieser Wert wird der Autosize übergeben. In dieser wird für das gerade gezeichnete Item der Listbox ermittelt, wie breit das Item im Endeffekt ist. Ist in flbHorzScrollWidth bereits ein größerer Wert als der gerade ermittelte, muss keine Änderung der ScrollWidth durchgeführt werden. Falls doch, so setzen wir ScrollWidth auf den ermittelten Wert und ändern die größe des horizontalen Scrollbalkens. (Die Perform-Methode tut das). Ausserdem übernimmt die Autosize-Prozedur das Zeichen des Items selber, was auch nötig ist, da die Listbox ja auf OwnerDraw steht. Das geschieht über FillRect und TextOut (Rechteck mit Farbe füllen, dann Text draufschreiben).
Funktioniert die Listbox dann noch so wie gewohnt? Ich meine, ist das markierte Item blau hinterlegt? Ich sehe nämlich nirgends im Code, dass im onDrawItem der Parameter State auf "Selected" überprüft wird :nixweiss:
Edit: Globale Integer-Variablen werden normalerweise mit 0 initialisiert, worauf man sich aber
nie verlassen sollte :mahn: In diesem Fall vermute ich, dass beim allerersten Aufruf der Autosize-Prozedur 0 in der Variable steht, was im Prinzip genau das ist, was wir hier wollen.
Dibelius - Sa 04.04.09 19:12
Ok, danke für die Aufklärung. Ich wusste nicht, dass globale Variablen mit 0 initialisiert werden. Ich hab das bisher immer eigenhändig getan und werde wohl auch dabei bleiben.
JayEff hat folgendes geschrieben : |
Funktioniert die Listbox dann noch so wie gewohnt? Ich meine, ist das markierte Item blau hinterlegt? Ich sehe nämlich nirgends im Code, dass im onDrawItem der Parameter State auf "Selected" überprüft wird :nixweiss: |
Ja, funktioniert alles. Ich stelle da kein Unterschied zur "normalen" Listbox fest, bis auf die Textausgabe, die man m.H. von Canvas relativ frei gestalten könnte, wenn man das will. Selected sieht man aber.
btw: in deiner Prozedur fehlt die Textausgabe noch.
JayEff - Sa 04.04.09 19:26
Dibelius hat folgendes geschrieben : |
btw: in deiner Prozedur fehlt die Textausgabe noch. |
Die ist auch nicht nötig, weil meine Prozedur ohne onDrawItem arbeitet und darum die Listbox auf lbStandard gestellt werden kann.
Dibelius hat folgendes geschrieben : |
Ich wusste nicht, dass globale Variablen mit 0 initialisiert werden. Ich hab das bisher immer eigenhändig getan und werde wohl auch dabei bleiben. |
Ist auch gut so, wie ich schon sagte:
JayEff hat folgendes geschrieben : |
[...]worauf man sich aber nie verlassen sollte :mahn: |
Selbst initialisieren ist schon für die Übersicht am besten, da weiß man immer genau, was drin steht, ohne zuerst zu überlegen. :zustimm:
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!