C’è un errore nel controllo della vista elenco Delphi quando si usa il disegno personalizzato?

QC # 101189

Sto cercando di disegnare una barra di avanzamento in un Delphi TListView come suggerito dalla risposta di NGLN a un’altra domanda SO . Funziona bene, a parte l’interazione con il tracciamento a caldo quando viene disegnato utilizzando il nuovo tema di esplorazione introdotto in Vista.

Il disegno di tracciamento a caldo e gli eventi di disegno personalizzato di Delphi sembrano interferire l’uno con l’altro. Ad esempio, il tipo di output che sto vedendo assomiglia a questo:

inserisci la descrizione dell'immagine qui

Il testo nella Colonna 1 dovrebbe leggere l’elemento 3 ma è cancellato. Sembra un bug nel wrapper Delphi per il controllo della lista, ma potrebbe anche essere che sto facendo qualcosa di sbagliato!

Anche se ho sviluppato questo in XE2, lo stesso comportamento si verifica nel 2010 e, presumibilmente, XE.

Ecco il codice per riprodurre questo comportamento:

File Pascal

 unit Unit1; interface uses Windows, Classes, Controls, Forms, CommCtrl, ComCtrls; type TForm1 = class(TForm) ListView: TListView; procedure FormCreate(Sender: TObject); procedure ListViewCustomDrawSubItem(Sender: TCustomListView; Item: TListItem; SubItem: Integer; State: TCustomDrawState; var DefaultDraw: Boolean); end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); begin ListView.RowSelect := True; ListView.Items.Add.Caption := 'Item 1'; ListView.Items.Add.Caption := 'Item 2'; ListView.Items.Add.Caption := 'Item 3'; end; procedure TForm1.ListViewCustomDrawSubItem(Sender: TCustomListView; Item: TListItem; SubItem: Integer; State: TCustomDrawState; var DefaultDraw: Boolean); var R: TRect; begin DefaultDraw := False; ListView_GetSubItemRect(Sender.Handle, Item.Index, SubItem, LVIR_BOUNDS, @R); Sender.Canvas.MoveTo(R.Left, R.Top); Sender.Canvas.LineTo(R.Right-1, R.Bottom-1); end; end. 

File di forma

 object Form1: TForm1 Caption = 'Custom Draw List View Bug' ClientHeight = 290 ClientWidth = 554 OnCreate = FormCreate object ListView: TListView Align = alClient Columns =  ViewStyle = vsReport OnCustomDrawSubItem = ListViewCustomDrawSubItem end end 

Questa è una soluzione alternativa per il comportamento difettoso piuttosto che essere una risposta alla domanda se c’è un bug nel VCL e alcuni pensieri.

La soluzione alternativa è impostare la modalità di background del contesto del dispositivo assegnata dal controllo comune per il ciclo di pittura degli oggetti a trasparente dopo aver eseguito il disegno personalizzato:

 procedure TForm1.ListViewCustomDrawSubItem(Sender: TCustomListView; Item: TListItem; SubItem: Integer; State: TCustomDrawState; var DefaultDraw: Boolean); var R: TRect; begin if not [CustomDrawing] then // <- If we're not gonna do anything do not Exit; // fiddle with the DC in any way DefaultDraw := False; ListView_GetSubItemRect(Sender.Handle, Item.Index, SubItem, LVIR_BOUNDS, @R); Sender.Canvas.MoveTo(R.Left, R.Top); Sender.Canvas.LineTo(R.Right-1, R.Bottom-1); SetBkMode(Sender.Canvas.Handle, TRANSPARENT); // <- will effect the next [sub]item end; 

In un ciclo di pittura [sotto] elemento, il dipinto viene sempre eseguito dall'alto verso il basso, gli articoli con un indice inferiore vengono inviati NM_CUSTOMDRAW notifica NM_CUSTOMDRAW prima di quelli con indici più alti. Quando il mouse viene spostato da una riga all'altra, è necessario ridisegnare due righe: quella che perde lo stato a caldo e quella che la guadagna. Sembrerebbe, quando il disegno personalizzato è in effetti, disegnare la riga che sta perdendo lo stato a caldo lascia la DC in uno stato indesiderabile. Questo non è un problema quando si sposta il mouse verso l'alto, perché quell'elemento viene disegnato per ultimo.

Disegno personalizzato I controlli ListView e TreeView sono diversi da quelli di disegno personalizzato e sono piuttosto complicati (vedere: Disegni personalizzati con i controlli Vista elenco e Vista albero ). Ma hai il pieno controllo dell'intero processo. Il codice nel caso NM_CUSTOMDRAW di TCustomListView.CNNotify in "comctrls.pas" del VCL è ugualmente complicato. Ma nonostante siano stati forniti un gruppo di gestori di disegni personalizzati (la metà dei quali è avanzata ), non si ha il controllo su ciò che fa il VCL. Ad esempio, non puoi restituire il CDRF_xxx che desideri o non puoi impostare il clrTextBk che desideri. La mia opinione parziale è che, c'è un problema di bug / design nel controllo della lista di Delphi , ma non ho nulla di più concreto di un'intuizione come nel trovare una soluzione alternativa.

Non ho la minima idea del rettangolo nero nella posizione del testo, ma il rilevamento a caldo mancante è dovuto a DefaultDraw := False; nel tuo codice. OnCustomDrawSubItem viene chiamato solo per il subitem <> 0 , quindi la prima colonna viene disegnata come predefinita mentre la seconda utilizza il codice. È ansible creare un disegno personalizzato della prima colonna con OnCustomDrawItem .