![]() |
Kann mir das einer mit ListBoxDrawItem und odFocused erklären
Wieder mal etwas wo ich 'ne viertel Stunde meines Lebens damit vergeudet habe, weil ich etwas schon zig mal gemacht habe und wo es dieses mal plötzlich nicht klappt.
Und zwar geht es drum den Fokus aus der ListBox zu entfernen, was ich übrigens schon einige Male gemacht habe. Anscheinend nur zufällig richtig. Überschreibe ich den Fokus mit DrawFocusRect vor Canvas.TextRect, bleibt er sichtbar, mache ich es danach, ist er weg. Anscheinend zeichnet TextRect den Fokus. Nur steht in der OH nichts von einem Flag der das bewirkt. Weiß einer mehr?
Delphi-Quellcode:
procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState); begin with (Control as TListbox) do begin //if odFocused in State then Canvas.DrawFocusRect(Rect); //klappt nicht Canvas.TextRect(Rect, Rect.Left, Rect.Top, Items[Index]); if odFocused in State then Canvas.DrawFocusRect(Rect); //klappt end; end; procedure TForm1.FormCreate(Sender: TObject); var i: Integer; begin //Beispielfüllung ListBox1.Style := lbOwnerDrawFixed; for i := 0 to 10 do ListBox1.Items.Add(DateTimeToStr(Now)); end; |
AW: Kann mir das einer mit ListBoxDrawItem und odFocused erklären
Ich habe mir die StrCtrls angeguckt, genauer das:
Delphi-Quellcode:
Was mich wundert ist, dass ich odFocused in State zum Beginn von ListBox OnDrawItem lösche. Trotz dem wird FocusRect gezeichnet.
procedure TCustomComboBox.CNDrawItem(var Message: TWMDrawItem);
var State: TOwnerDrawState; begin with Message.DrawItemStruct^ do begin State := TOwnerDrawState(LongRec(itemState).Lo); ... if odFocused in State then DrawFocusRect(hDC, rcItem); FCanvas.Handle := 0; end; end; |
AW: Kann mir das einer mit ListBoxDrawItem und odFocused erklären
Auf ein Neues. Ich hab mich den Problem noch Mal gewidmet, aber bin wieder gescheiter mit der Erklärung. Es geht um den Fokus in der ListBox. Das ist gepunktete Rahmen um den selektierten Item. Nicht die Hintergrundfarbe, sondern der Rahmen.
Meine Frage ist: wann und wie wird in einer Listbox der Fokus-Rand gezeichnet? Die Annahme, dass er zuerst in den OnDrawItem Prozedur gezeichnet wird, stimmt nicht mit den Beobachtungen. Würde er bereits gezeichnet in die OnDrawItem Prozedur kommen, würde FillRect ihn hier löschen:
Delphi-Quellcode:
FillRect löscht den ganzen Canvas in Rect Bereich. Da müßte er theoretisch den Fokus mit löschen. Tut er aber nicht.
procedure TForm1.FormCreate(Sender: TObject);
var s: String; i, k: Integer; begin ListBox1.Style := lbOwnerDrawFixed; {Inhalt generieren} for i := 0 to 10 do ListBox1.Items.Add(StringOfChar('a', 30)); end; procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState); var ListBox: TListBox; begin ListBox := (Control as TListBox); if odSelected in State then begin ListBox.Canvas.Font.Color := clWindowText; ListBox.Canvas.Brush.Color := clSilver; end; //Löscht den Item-Canvas ListBox.Canvas.FillRect(Rect); //Textausgabe ListBox.Canvas.TextOut(Rect.Left + 2, Rect.Top, ListBox.Items[Index]); end; Hier noch mal das gleiche in grün, nur dieses Mal mit der Fokus-Lösch-Funktion (auch wenn es vor FillRect anzuordnen unsinnig ist, aber eben für den Test).
Delphi-Quellcode:
Fokus ist komplett da obwohl er zuerst gelöscht wurde und dann noch Canvas gelöscht wurde.
procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState); var ListBox: TListBox; begin ListBox := (Control as TListBox); if odSelected in State then begin ListBox.Canvas.Font.Color := clWindowText; ListBox.Canvas.Brush.Color := clSilver; end; //Überzeichnet den alten Fokus weg if odFocused in State then ListBox.Canvas.DrawFocusRect(Rect); //Löscht den Item-Canvas ListBox.Canvas.FillRect(Rect); //Textausgabe ListBox.Canvas.TextOut(Rect.Left + 2, Rect.Top, ListBox.Items[Index]); end; Hier ist DrawFocusRect nach FillRect und und vor der Textausgabe.
Delphi-Quellcode:
Der gepunktete Rand ist jetzt zwar um den selektierten Bereich weg, aber über und unter dem Text immer noch da.
procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState); var ListBox: TListBox; begin ListBox := (Control as TListBox); if odSelected in State then begin ListBox.Canvas.Font.Color := clWindowText; ListBox.Canvas.Brush.Color := clSilver; end; //Löscht den Item-Canvas ListBox.Canvas.FillRect(Rect); //Überzeichnet den alten Fokus weg if odFocused in State then ListBox.Canvas.DrawFocusRect(Rect); //Textausgabe ListBox.Canvas.TextOut(Rect.Left + 2, Rect.Top, ListBox.Items[Index]); end; Nun der letzte Versuch. Dieses mal ist DrawFocusRect die letzte Funktion und hinter der Textausgabe:
Delphi-Quellcode:
Hier ist der gepunktete Fokus-Rand komplett weg. Und ich stelle mit seit Monaten (nicht permanent, aber gelegentlich wieder) die Frage wieso. Die Lösung ist wahrscheinlich wieder mal ganz einfach, aber ich verstehe es nicht. Die Funktion die den Rand in der ListBox zeichnet ist wohl CNDrawItem. Und so wie ich die lese liefert sie die Anfangs-Einstellungen für OnDrawItem. Es sieht fast so aus als ob er nach den Ende noch mal gezeichnet wird. Wie gesagt, ist wahrscheinlich ganz einfache Erklärung, nur erkenne ich sie nicht.
procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState); var ListBox: TListBox; begin ListBox := (Control as TListBox); if odSelected in State then begin ListBox.Canvas.Font.Color := clWindowText; ListBox.Canvas.Brush.Color := clSilver; end; //Löscht den Item-Canvas ListBox.Canvas.FillRect(Rect); //Textausgabe ListBox.Canvas.TextOut(Rect.Left + 2, Rect.Top, ListBox.Items[Index]); //Überzeichnet den alten Fokus weg if odFocused in State then ListBox.Canvas.DrawFocusRect(Rect); end; |
AW: Kann mir das einer mit ListBoxDrawItem und odFocused erklären
Tstststs ... anstatt stundenlang die Haare zu raufen und irgendwas auszuprobieren, hätten ein paar gezielte Strg-Clicks auf DrawFocusRect zunächst hierhin gebracht:
Delphi-Quellcode:
Dann hierhin:
unit Vcl.Graphics;
... procedure TCanvas.DrawFocusRect(const Rect: TRect); begin Changing; RequiredState([csHandleValid, csBrushValid]); Winapi.Windows.DrawFocusRect(FHandle, Rect); Changed; end;
Delphi-Quellcode:
Und schließlich hierhin:
unit Winapi.Windows;
... function DrawFocusRect; external user32 name 'DrawFocusRect'; ![]() Und was finden wir da? Zitat:
|
AW: Kann mir das einer mit ListBoxDrawItem und odFocused erklären
Nun, ich gebe zu, dass ich mir alles angeguckt habe, nur nicht die DrawFocusRect Funktion. Hielt ich nicht für nötig.
Trotzdem sehe ich hier nicht die Antwort auf meine Verwirrung. Vielleicht sehe ich es einfach nur nicht. Das DrawFocusRect Xor zeichnet ist mir klar und auch, dass ein erneutes Zeichnen es wieder löscht. Das habe ich ja auch gemacht. In einem der Beispiel habe ich es vor FillRect gemacht, dann mal danach und zuletzt ganz am Ende. Wie auch, nach meinem Verständnis sollte von dem Rahmen schon in den ersten Beispielen nichts mehr übrig sein. Oder habe ich dich missverstanden? |
AW: Kann mir das einer mit ListBoxDrawItem und odFocused erklären
Das sich 2x XOR aufhebt, funktioniert nur dann, wenn ich das, was das erste XOR verändert hat, vor dem 2. XOR nicht verändere.
Das hast du aber immer gemacht (mit FillRect, TextRect, etc.) Darum ist es am sichersten alles auf den Canvas zu klatschen, ganz am Ende DrawFocusRect (ja, den Rahmen malen lassen) und das Control macht auch nochmal DrawFocusRect und schwupps ist der Rahmen weg |
AW: Kann mir das einer mit ListBoxDrawItem und odFocused erklären
Das mache ich ja auch, ist ja inzwischen auch nicht das Problem. Und ich muß zugeben, dass ich damals durch harte Versuche selbst drauf gekommen bin. Zuletzt habe ich Blut gespuckt. Denn entweder hatte nie ein anderer je dieses Problem, weil das Entfernen des Rahmens so simpel ist und jeder einen angeborenen Instinkt hat es immer zuletzt zu plazieren, oder es ich weiß es nicht.
Wenn du dir aber das Beispiel 2 anguckst, dann lösche ich da vor FillRect den Rahmen. Aber auch das sehe ich nicht als das Problem an, denn Xor zeichnen verändert nicht die Eigenschaften der Pixel. Wenn also zu Anfang der Prozedur bereits ein Rahmen existiert, dann wird er spätestens nach FillRect weg sein. Xor hin, Xor her. Wenn ich also (nach meiner Logik) am Ende der Prozedur selbst ein Xor Rahmen zeichne und das System danach wieder einen Xor Rahmen zeichnet, dann ist er weg. Somit müßte nach meinem Verständnis nach dem Ende der Prozedur wieder ein Xor Rahmen gezeichnet werden. Nur ich sehe nicht wo. |
AW: Kann mir das einer mit ListBoxDrawItem und odFocused erklären
Delphi-Quellcode:
unit Unit3;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TListbox=Class(StdCtrls.TListBox) procedure CNDrawItem(var Message: TWMDrawItem); message CN_DRAWITEM; End; TForm3 = class(TForm) ListBox1: TListBox; procedure FormCreate(Sender: TObject); procedure ListBox1DrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState); private Fcanvas:TCanvas; { Private-Deklarationen } public { Public-Deklarationen } end; var Form3: TForm3; implementation {$R *.dfm} procedure TForm3.FormCreate(Sender: TObject); var i:Integer; begin Fcanvas := TCanvas.Create; for I := 1 to 10 do Listbox1.Items.Add('testdfdsf'); end; procedure TForm3.ListBox1DrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState); begin with TListBox(Control).Canvas do begin if odSelected in State then Brush.Color := clLime else Brush.Color := $EEEEEE; Fillrect(rect); Textout(rect.Left,rect.Top,TListBox(Control).Items[Index]); end; end; { TListbox } procedure TListbox.CNDrawItem(var Message: TWMDrawItem); var w:TOwnerDrawState; begin w := TOwnerDrawState(LoWord(Message.DrawItemStruct^.itemState)); Exclude(w , odFocused); Message.DrawItemStruct^.itemState := Message.DrawItemStruct^.itemState and ( $FFFF0000 + Word(w)); inherited; end; end. |
AW: Kann mir das einer mit ListBoxDrawItem und odFocused erklären
Zitat:
Delphi-Quellcode:
unit Vcl.StdCtrls;
... procedure TCustomListBox.CNDrawItem(var Message: TWMDrawItem); var State: TOwnerDrawState; begin with Message.DrawItemStruct{$IFNDEF CLR}^{$ENDIF} do begin ... // unwichtiger Krams ... wenigstens für diese Betrachtung :o) if Integer(itemID) >= 0 then DrawItem(itemID, rcItem, State) // <-- hier wird auch das OnDrawItem-Event ausgelöst else FCanvas.FillRect(rcItem); if (odFocused in State) and not TStyleManager.IsCustomStyleActive then DrawFocusRect(hDC, rcItem); // <-- da ist der schlimme Finger FCanvas.Handle := 0; end; end; |
AW: Kann mir das einer mit ListBoxDrawItem und odFocused erklären
Siehst du, ich wußte, dass es einfach ist. Wo der "Schlimme Finger" war/ist, wußte/weiß ich, ich hab nur bei den zwei Dutzend mal wo ich mir die Prozedur angesehen habe die DrawItem Zeile nicht wahrgenommen.
Ok, Frage beantwortet. Danke euch. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 14:10 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