Как избавиться от ограничения вертикальной прокрутки TListBox?
Я реализовал просмотрщик журнала, используя TListBox
в виртуальном режиме.
Он отлично работает (для всего кода, который я написал), отображает содержимое, как и ожидалось (я даже легко добавил горизонтальную полосу прокрутки), но я предполагаю, что достиг некоторого предела вертикальной полосы прокрутки.
То есть, когда я прокручиваю вертикальную полосу сверху вниз, она не прокручивает содержимое до конца списка, а только до некоторого предела.
Знаете ли вы возможность избавиться от этого ограничения? Я пробовал с SetScrollInfo
, но это не сработало, поскольку лимит звучит не на полосе прокрутки, а в TListBox
сам.
Я знаю решение создания выделенного TCustomControl
: в этом случае SetScrollInfo
будет работать как положено. Но кто-нибудь знает о решении / уловке, чтобы все еще использовать TListBox
?
Редактировать: чтобы было понятно - я не прошу (стороннее) компонентное решение, но хочу знать, есть ли какое-то низкоуровневое сообщение GDI для отправки в стандарт TListBox
переопределить этот предел. Если его нет, я пойду на выделенный TCustomControl
решение.
Вот код, использующий TSCROLLINFO:
procedure ScrollVertHuge(Handle: HWND; count: integer);
var Scroll: TSCROLLINFO;
begin
Scroll.cbSize:= sizeof(Scroll);
Scroll.fMask := SIF_DISABLENOSCROLL or SIF_RANGE;
Scroll.nMin := 0;
Scroll.nMax := count;
SetScrollInfo(Handle,SB_VERT,Scroll,false);
end;
Чтобы уточнить проблему: добавление и рисование обоих работают, конечно (мой инструмент работает как положено), но что не работает, так это перетаскивание вертикальной полосы прокрутки. Я переименовал название вопроса и избавился от устаревших статей MSDN, которые сбивают с толку.
2 ответа
Приведенное ниже, вероятно, следует рассматривать в качестве обходного пути для некорректного поведения ОС, поскольку, если темы не включены, стандартная оконная процедура элемента управления списком довольно хорошо справляется с отслеживанием большого пальца. По какой-то причине, когда темы включены (тест здесь показан с Vista и более поздними версиями), элемент управления, похоже, полагается на данные положения прокрутки размером в Word WM_VSCROLL
,
Во-первых, простой проект для дублирования проблемы, ниже владелец рисовать виртуальный (lbVirtualOwnerDraw
) список со списком примерно 600000 элементов (поскольку данные элементов не кэшируются, заполнение этого поля не займет много времени). Высокий список будет удобен для простого отслеживания поведения:
type
TForm1 = class(TForm)
ListBox1: TListBox;
procedure ListBox1Data(Control: TWinControl; Index: Integer;
var Data: string);
procedure ListBox1DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
procedure FormCreate(Sender: TObject);
end;
[...]
procedure TForm1.FormCreate(Sender: TObject);
begin
ListBox1.Count := 600000;
end;
procedure TForm1.ListBox1Data(Control: TWinControl; Index: Integer;
var Data: string);
begin
Data := IntToStr(Index) + ' listbox item number';
end;
procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
begin
// just simple drawing to be able to clearly see the items
if odSelected in State then begin
ListBox1.Canvas.Brush.Color := clHighlight;
ListBox1.Canvas.Font.Color := clHighlightText;
end;
ListBox1.Canvas.FillRect(Rect);
ListBox1.Canvas.TextOut(Rect.Left + 2, Rect.Top + 2, ListBox1.Items[Index]);
end;
Чтобы увидеть проблему, просто прокрутите полосу прокрутки большим пальцем, и вы заметите, как элементы обертываются, чтобы начинаться с начала для каждого 65536, как описано Арно в комментариях к вопросу. И когда вы отпустите большой палец, он будет привязан к элементу в верхней части High(Word)
,
Ниже перехваты обхода WM_VSCROLL
на элементе управления и выполняет позиционирование большого пальца и элемента вручную. В этом примере для простоты используется промежуточный класс, но подойдет любой другой метод подкласса:
type
TListBox = class(stdctrls.TListBox)
private
procedure WMVScroll(var Msg: TWMVScroll); message WM_VSCROLL;
end;
[...]
procedure TListBox.WMVScroll(var Msg: TWMVScroll);
var
Info: TScrollInfo;
begin
// do not intervene when themes are disabled
if ThemeServices.ThemesEnabled then begin
Msg.Result := 0;
case Msg.ScrollCode of
SB_THUMBPOSITION: Exit; // Nothing to do, thumb is already tracked
SB_THUMBTRACK:
begin
ZeroMemory(@Info, SizeOf(Info));
Info.cbSize := SizeOf(Info);
Info.fMask := SIF_POS or SIF_TRACKPOS;
if GetScrollInfo(Handle, SB_VERT, Info) and
(Info.nTrackPos <> Info.nPos) then
TopIndex := TopIndex + Info.nTrackPos - Info.nPos;
end;
else
inherited;
end;
end else
inherited;
end;
Для пользовательского средства просмотра журнала, которое я написал, я использую TListView
в виртуальном режиме, а не TListBox
, Прекрасно работает, без ограничений 32K, не нужно возиться с SetScrollInfo()
совсем. Просто установите Item.Count
а остальное обрабатывается автоматически. Это даже имеет OnDataHint
событие, которое можно использовать для оптимизации доступа к данным, позволяя загружать только те данные, которые TListView
на самом деле нужно. Вы не получите это с TListBox
,