Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get rid of TListBox vertical scroll limit?

I've implement a log viewer using a TListBox in virtual mode.

It works fine (for all the code I wrote), displays the content as expected (I even added an horizontal scrollbar easily), but I guess I've reached the some kind of limit of the vertical scrollbar.

That is, when I scroll the vertical bar from the top to the bottom, it will not scroll the content to the end of the list, but only to some limit.

Do you know any possibility to get rid of this limit? I tried with SetScrollInfo, but it didn't work since the limit sounds to be not in the scrollbar, but in the TListBox itself.

I know the solution of creating a dedicated TCustomControl: in this case, the SetScrollInfo will work as expected. But does anyone know about a solution/trick to still use TListBox?

Edit: to make it clear - I don't ask for a (third-party) component solution, but to know if there is some low-level GDI message to send to the standard TListBox to override this limit. If there is none, I'll go to the dedicated TCustomControl solution.

Here is the code using 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;

To precise the issue: Adding and drawing both work, of course (my tool works as exepected), but what does not work is the vertical scrollbar dragging. I renamed the title of the question, and got rid of the deprecated MSDN articles, which are confusing.

like image 507
Arnaud Bouchez Avatar asked Aug 22 '11 08:08

Arnaud Bouchez


People also ask

How to disable the vertical scroll bar?

To disable scroll bars in CSS, you can use “overflow-x”, “overflow-y”, and the “overflow” properties. The overflow-x property is specifically utilized for disabling the vertical scroll bar, and the overflow-y property to disable horizontal scroll bars.

How to remove Scrollbar from Listbox?

To fix this, change the "Column Width" property under the format tab to something really small, then play around with this property and the size of the listbox control until you get it how you want, and no scroll bars are displayed. Save this answer. Show activity on this post.

How to remove Scroll in dropdown List?

Select the Styles tab, and add the following style properties to the Style Properties Applied text box: overflow-y: hidden; background-image: none; overflow-y: hidden disables the scroll bar and background-image: none removes the down arrow. Note: Height is already set to 100px.

How to remove vertical Scrollbar in ag grid?

Debounce Vertical Scroll To debounce the vertical scroll, set grid property debounceVerticalScrollbar=true .


2 Answers

The below probably should be considered as a work-around for defective OS behavior, since, unless themes are enabled, the default window procedure of a listbox control handles thumb-tracking quite well. For some reason, when themes are enabled (test here shows with Vista and later), the control seems to rely upon the Word sized scroll position data of WM_VSCROLL.

First, a simple project to duplicate the problem, below is an owner draw virtual (lbVirtualOwnerDraw) list box with some 600,000 items (since item data is not cached it doesn't take a moment to populate the box). A tall listbox will be good for easy following the behavior:

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;


To see the problem just thumb-track the scroll bar, you'll notice how the items are wrapped to begin from the start for every 65536 one as described by Arnaud in the comments to the question. And when you release the thumb, it will snap to an item in the top High(Word).


Below workaround intercepts WM_VSCROLL on the control and performs thumb and item positioning manually. The sample uses an interposer class for simplicity, but any other sub-classing method would do:

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;
like image 59
Sertac Akyuz Avatar answered Oct 16 '22 03:10

Sertac Akyuz


For a custom log viewer I wrote, I use a TListView in virtual mode, not a TListBox. Works great, no 32K limits, no need to fiddle with SetScrollInfo() at all. Just set the Item.Count and the rest is handled automatically. It even has an OnDataHint event that can be used to optimize data access by letting you load only the data that the TListView actually needs. You don't get that with a TListBox.

like image 1
Remy Lebeau Avatar answered Oct 16 '22 03:10

Remy Lebeau