How to get rid of the vertical scroll restriction of a TListBox?

I am implementing a log view using TListBox in virtual mode .

It works fine (for all the code I wrote), displays the content as expected (I even added a horizontal scrollbar), but I think I reached my limit in the vertical scrollbar.

That is, when I look at a vertical bar from top to bottom, it will not scroll to the end of the list, but only to a certain limit.

Do you know any opportunity to get rid of this limit? I tried with SetScrollInfo , but this did not work, since the restriction does not sound in the scroll bar, but in the TListBox itself.

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

Edit: to make this clear, I am not asking for solutions for third-party developers, but I know if there is a low-level GDI message to be sent by the standard TListBox to override this restriction. If they are not, I will 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 clarify the problem: adding and drawing both functions, of course (my tool works as exepected), but what doesn't work is dragging the vertical scrollbar. I renamed the question title and got rid of obsolete MSDN articles that are confusing.

+7
source share
2 answers

Below, it should probably be considered a workaround for the defective behavior of the OS, because if the themes are not included, the standard window procedure of the list control does fine with finger tracking. For some reason, when themes are turned on (the test is shown here with Vista and later), the control relies on Word scroll position data of size WM_VSCROLL .

First, a simple project to duplicate the problem below is a list of the virtual (owner) drawing of the owner ( lbVirtualOwnerDraw ) with 600,000 elements (since the data for the element is not cached, it does not take a minute to fill in the field). A high list will be good for easily 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 drag the mouse over the scroll bar, you will notice how the elements are wrapped to start from the beginning for each 65536, as described by Arno in the comments on the question. And when you release your thumb, it will be attached to the item at the top of High(Word) .


Below the workaround, it hooks WM_VSCROLL on the control and manually adjusts the thumb and position. The sample uses the interpolator class for simplicity, but any other subclass method:

 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; 
+7
source

For the custom log viewer that I wrote, I use TListView in virtual mode, not TListBox . Works great, doesn't limit 32K, no need to mess around with SetScrollInfo() . Just set Item.Count , and the rest are processed automatically. It even has an OnDataHint event that can be used to optimize data access, allowing you to load only the data that TListView really needs. You will not get this with TListBox .

+1
source

All Articles