Есть ли ошибка в управлении представлением списка Delphi при использовании пользовательского чертежа?
QС# 101189
Я пытаюсь настроить график выполнения в Delphi TListView
, как было предложено ответом NGLN на другой вопрос SO. Это отлично работает, кроме взаимодействия с горячим отслеживанием при рисовании с использованием новой темы проводника, представленной в Vista.
Горячая картинка отслеживания и пользовательские события рисования Delphi, по-видимому, мешают друг другу. Например, вид вывода, который я вижу, выглядит следующим образом:
![enter image description here]()
Текст в столбце 1 должен быть прочитан в пункте 3, но уничтожен. Это похоже на ошибку в оболочке Delphi на элемент управления списком, но в равной степени может быть и то, что я делаю что-то неправильно!
Хотя я разрабатывал это в XE2, такое же поведение происходит в 2010 году и, предположительно, XE.
Здесь приведен код для воспроизведения этого поведения:
Паскаль
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.
Файл формы
object Form1: TForm1
Caption = 'Custom Draw List View Bug'
ClientHeight = 290
ClientWidth = 554
OnCreate = FormCreate
object ListView: TListView
Align = alClient
Columns = <
item
Caption = 'Column 1'
Width = 250
end
item
Caption = 'Column 2'
Width = 250
end>
ViewStyle = vsReport
OnCustomDrawSubItem = ListViewCustomDrawSubItem
end
end
Ответы
Ответ 1
Это обходной путь для дефектного поведения, а не ответ на вопрос, есть ли ошибка в VCL и несколько мыслей.
Обходной путь заключается в том, чтобы установить фоновый режим контекста устройства, назначенный общим элементом управления, для рисования элемента cyle до прозрачного после выполнения пользовательского чертежа:
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;
В цикле краски [sub] item картина всегда выполняется сверху вниз, элементы, имеющие более низкий индекс, отправляются уведомлением NM_CUSTOMDRAW
до тех, у кого более высокие индексы. Когда мышь перемещается из одной строки в другую, нужно перерисовывать две строки - одну, которая теряет горячее состояние, и тот, кто ее набирает. Казалось бы, когда пользовательский чертеж не действует, рисование строки, которая теряет горячее состояние, оставляет DC в нежелательном состоянии. Это не проблема при перемещении мыши вверх, потому что этот элемент набирается последним.
Пользовательский чертеж Элементы управления ListView и TreeView отличаются от пользовательских чертежей другими элементами управления и несколько сложными (см.: Пользовательский чертеж с элементами управления List-View и Tree-View). Но вы полностью контролируете весь процесс. Код в NM_CUSTOMDRAW
случае TCustomListView.CNNotify
в 'comctrls.pas' VCL одинаково сложный. Но, несмотря на то, что вам предоставлена куча пользовательских обработчиков чертежей (половина из них продвинута), вы не можете контролировать, что делает VCL. Например, вы не можете вернуть CDRF_xxx
, который хотите, или вы не можете установить clrTextBk
, который вы хотите. Мое предубежденное мнение состоит в том, что в элементе управления представлением списка Delphi есть проблема с ошибкой/дизайном, но у меня нет ничего более конкретного, чем интуиция, как в поиске обходного пути.
Ответ 2
У меня нет подсказки для черного прямоугольника в текстовой позиции, но недостающее горячее отслеживание связано с DefaultDraw := False;
в вашем коде. OnCustomDrawSubItem
вызывается только для subitem <> 0
, поэтому первый столбец отображается по умолчанию, а второй использует ваш код. Пользовательский чертеж первого столбца можно сделать с помощью OnCustomDrawItem
.