OnDrawItem() für ListBox
-
Hallo alle zusammen,
ich habe wieder mal ein Problem, und zwar möchte gerne im ListBox neben den Sring, links Grafik anzeigen lassen, aber richtig mir die Klarheit verschaffen konnte ich bisher nicht, trotz dem Beispiel unter BCB Hilfe.
Ich habe mir folgenden Beispiel geschrieben:
void __fastcall TForm1::Button1Click(TObject *Sender) { static int zh = 0; TCanvas *grafikzeiger = ListBox1->Canvas; Graphics::TBitmap *bild = new Graphics::TBitmap(); bild->LoadFromFile(ExtractFilePath(Application->ExeName)+ "Ordner_16.bmp"); bild->Transparent = true; for(int i = 0; i<10;i++) { grafikzeiger->Draw(2,zh, bild); //Grafik anzeigen grafikzeiger->TextOut(bild->Width+6,zh+2,"Pfad des Ordners!"); //Text anzeigen zh += 17; } }
nach dem Buttonklick werden in ListBox 10 einträge reingeschrieben, wird alles dargestellt wie ich das vor hatte, aber ich kann kein einzelnen Eintrag markieren und ListBox->Count ist gleich null, hatte über Label anzeigen lassen.
Allerdingst funktioniert diese Eintragung nur nach dem Buttonklick und nicht wenn ich den Code unter Ereignis OnDrawItem packe.Kennt jemand einfache Erklärung dafür?
Man möchte gerne die Textpfade(Strings) in ListBox handhaben wie bisher bei
ListBox->Style = lbStandard, oder ist das im Zusammenhang mit Grafik nicht möglich?
-
Nun ja, die Erklärung ist einfach: Du zeichnest nur auf den Canvas. Damit erzeugst Du natürlich keine Einträge in der ListBox. Du mußt die ListBox weiterhin mit Daten befüllen und nur die eigentliche Grafikausgabe und Textausgabe selbst übernehmen. In Ereignis OnDrawItem wird Dir ein Rect übergeben, welches den Dimensionen des Eintrages entspricht. Dort hinein mußt Du die Ausgabe machen.
-
Hallo Joe_M,
in echtem Fall wird bei mir in OnCreate() das ListBox durch LoadFromFile() befüllt, aber wie kann ich hinterher die optische Darstellung ändern?
Auf den Strings noch zusätzlich Canvas Zeichenen?
Wenn ich ListBox->Style = lbOwnerDrawFixed setze, sieht genau so aus wie vorher?
Der Beispiel unter BCB verstehe ich gaar nicht, z.b. was bedeutet diese Zuweisung:pBitmap = (Graphics::TBitmap *)((TListBox *)Control)->Items->Objects[Index];
oder
TCanvas *pCanvas = ((TListBox *)Control)->Canvas;
-
Sorry, wenn ich das so schreibe, aber wenn du die beiden Casts nicht verstehst, so solltes du dir erstmal die Grundlagen von C++ aneignen, anstatt schon gleich irgendwelches GUI-Spezielles zu programmieren.
-
Fangen wir mal hiermit an:
aesse schrieb:
Der Beispiel unter BCB verstehe ich gaar nicht, z.b. was bedeutet diese Zuweisung:
pBitmap = (Graphics::TBitmap *)((TListBox *)Control)->Items->Objects[Index];
Hier ist in dem Object-Array der ListBox jeweils die entsprechende Grafik hinterlegt. Das ist eine prime Idee, wenn man verschiedene Grafiken hat. Eigentlich wird nur das void-Object wieder einem TBitmap-Object gecastet. Die Control-Casterei ist für den Fall, wenn Du mehrere Listboxen hast, die alle ein und dieselbe Draw-Item-Funktion verwenden.
TCanvas *pCanvas = ((TListBox *)Control)->Canvas;
Das ist nur um sich die ellenlange Tipperei an den Stellen zu sparen, in denen auf den Canvas zugegriffen wird.
aesse schrieb:
in echtem Fall wird bei mir in OnCreate() das ListBox durch LoadFromFile() befüllt, aber wie kann ich hinterher die optische Darstellung ändern?
Auf den Strings noch zusätzlich Canvas Zeichenen?
Wenn ich ListBox->Style = lbOwnerDrawFixed setze, sieht genau so aus wie vorher?Die Methode DrawListItem wird für jeden Eintrag in der ListBox aufgerufen, wenn die ListBox neu gezeichnet werden muß.
Ich hab Deinen Source mal ein bißchen abgeändert, sollte so funktionieren:
void __fastcall TForm1::ListBox1DrawItem(TWinControl *Control, int Index, TRect &Rect, TOwnerDrawState State) { // Das Erzeugen und Laden (und auch das Löschen) des BMPs sollte vorzugsweise nicht für jeden Zeichenvorgang durchgeführt werden, sondern einmalig z.B. im Konstruktor des Forms Graphics::TBitmap* bmp = new Graphics::TBitmap(); bmp->LoadFromFile(Application->ExeName)+ "Ordner_16.bmp"); if (State.Contains(odSelected)) ListBox1->Canvas->DrawFocusRect(Rect); ListBox1->Canvas->FillRect(Rect); ListBox1->Canvas->Draw(Rect.left + 2, Rect.top + ((Rect.Height() - bmp->Height) / 2), bmp); ListBox1->Canvas->TextOutA(Rect.left + 4 + bmp->Width, Rect.top + ((Rect.Height() - ListBox1->Canvas->TextHeight(ListBox1->Items->Strings[Index])) / 2 ), ListBox1->Items->Strings[Index]); delete bmp; }
Die wichtigen Zeilen (der Block in der Mitte) macht folgendes:
Wenn Eintrag markiert, den Fokus-Rahmen zeichnen
Komplette Zeichenfläche des aktuellen Bereichs löschen.
Grafik ausgeben (2 Pixel rechts vom Rand, in der Höhe mittig)
Text ausgeben (weitere 2 Pixel rechts von der Grafik, in der Höhe mittig)Wird das so klarer?
-
Vielen großen Dank!!!
Dennnoch habe ich noch eine Frage, du hatest erwännt, dass Laden, Erzeugen und auch Löschen im Konstruktor bevorzugt ist.
Mir geht es um Löschen, wenn im Konstruktor z.b. Grafik geladen oder erzeugt wird darf ich die doch nicht nach die Erzeugung löschen, sonst erfüllt es unseren Zweck nicht oder? Oder denke ich falsch? Oder gibt es bestimmte Technik dafür?
-
Das Löschen des Bitmaps gehört selbstverständlich in den Destruktor. Ich war nur zu faul, das explizit zu schreiben...