Дмитрий Трагер дата публикации 24-07-2007 09:32 TListView с сортировкой по любому столбцу
Решил я написать компонент, унаследованный от TListView, с
сортировкой элементов списка, была такая задача. Причём, не с такой
сортировкой, как у родителя, а чтобы можно было по любому индексу
подэлементов сортировать весь список. Но главным было то, что эта
сортировка ведётся при кликах на заголовке в режиме отображения
vsReport, и при этом на текущей секции заголовка должна рисоваться
стрелка, указывающая направление сортировки.
По большому счёту ничего выдающегося не сделано. Но поскольку
компонент оказался очень полезным, а ничего добротного я на эту тему
не встречал в интернете, решил отметить, так сказать, свой маленький успех.
По части сортировки ничего сложного нет. Единственный примечательный
момент здесь в том, что перед сравнением двух элементов списка
вызывается событие типа
TCompareNotifyEvent = procedure (Sender: TObject; Item: TListItem;
Index: integer; var Value: variant) of object
Оно возвращает значение, используемое в дальнейшем при сравнении, а по
умолчанию
туда передаётся Caption самого элемента или подэлемента в зависимости
от индекса. Поэтому если обработчик события не определён, сортировка
будет сравнивать строки.
Самое интересное заключалось в рисовании стрелок. Возможно, кто-то
делает то, о чём будет рассказано далее каждый день, но я сам до этого
никогда такими вещами не занимался, хотя с WinAPI более или менее
знаком. Поэтому для меня всё было в новинку. Я до этого рисовал
такие стрелки через Canvas на обыкновенном THeaderControl. Здесь, в
тандартном TListView, есть
такой же заголовок, но его явно никак не получить. В Vcl я подсмотрел
способ получения Handle заголовка окна и переопределения его оконной
процедуры:
procedure TDLExListView.WMParentNotify(var Message: TWMParentNotify);
begin
with Message do
if (Event = WM_CREATE) and (FHeaderHandle = 0)
then begin
FHeaderHandle := ChildWnd;
FDefHeaderProc := Pointer(GetWindowLong(FHeaderHandle, GWL_WNDPROC));
SetWindowLong(FHeaderHandle, GWL_WNDPROC, LongInt(FHeaderInstance));
end;
inherited;
end
После этого в собственной оконной процедуре, на которую указывает
FHeaderInstance и из которой обязательно вызывается родная процедура,
остаётся перехватом сообщения WM_PAINT нарисовать то, что надо.
Сам указатель FHeaderInstance инициализируется в конструкторе компонента:
FHeaderInstance := Classes.MakeObjectInstance(HeaderWndProc)
И раз уж на то пошло, то рисовать стрелки я решил функциями WinAPI,
передавая туда графический контекст заголовка, который получается так:
headDC := GetWindowDC(FHeaderHandle), -
а освобождается соответственно ReleaseDC(FHeaderHandle, headDC).
К архиву приложен ещё один файл главного модуля, где показано, как это
можно сделать, сконструировав канву и привzзав её к заголовку. Такой
способ видится более простым. Но по факту является более долгим. И
неинтереcным.
Прилагаю архив с компонентом и тестовым приложением.
(c) Дмитрий BBT Трагер,
Logic Systems Company.
К материалу прилагаются файлы:
[TListView] [Поиск и сортировка]
Обсуждение материала [ 30-07-2007 12:03 ] 9 сообщений |