Как избавиться от ограничения вертикальной прокрутки TListBox? - PullRequest
7 голосов
/ 22 августа 2011

Я реализовал средство просмотра журнала, используя 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 ]

7 голосов
/ 23 августа 2011

Нижеследующее, вероятно, следует рассматривать в качестве обходного пути для некорректного поведения ОС, поскольку, если темы не включены, стандартная оконная процедура элемента управления списком довольно хорошо справляется с отслеживанием большого пальца.По какой-то причине, когда темы включены (тест здесь показан с Vista и более поздними версиями), элемент управления, похоже, полагается на данные положения прокрутки размером в Word WM_VSCROLL.

Во-первых, простой проект для дублирования проблемыниже приведен виртуальный (lbVirtualOwnerDraw) список со списком владельцев, содержащий около 600 000 элементов (поскольку данные элементов не кэшируются, для заполнения этого поля не требуется много времени).Высокий список будет удобен для простого отслеживания поведения:

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;
1 голос
/ 23 августа 2011

Для пользовательского средства просмотра журналов, которое я написал, я использую TListView в виртуальном режиме, а не TListBox.Прекрасно работает, без ограничений 32K, не нужно возиться с SetScrollInfo().Просто установите Item.Count, а остальное обрабатывается автоматически.У него даже есть событие OnDataHint, которое можно использовать для оптимизации доступа к данным, позволяя загружать только те данные, которые действительно нужны TListView.Вы не получите это с TListBox.

...