[英]How to get rid of TListBox vertical scroll limit?
I've implement a log viewer using a TListBox
in virtual mode . 我在虚拟模式下使用
TListBox
实现了一个日志查看器 。
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. 我尝试使用
SetScrollInfo
,但它没有工作,因为限制听起来不在滚动条中,而是在TListBox
本身。
I know the solution of creating a dedicated TCustomControl
: in this case, the SetScrollInfo
will work as expected. 我知道创建专用
TCustomControl
的解决方案:在这种情况下, SetScrollInfo
将按预期工作。 But does anyone know about a solution/trick to still use TListBox
? 但有没有人知道仍然使用
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. 编辑:说清楚 - 我不要求(第三方)组件解决方案,但要知道是否有一些低级GDI消息要发送到标准
TListBox
以覆盖此限制。 If there is none, I'll go to the dedicated TCustomControl
solution. 如果没有,我将转到专用的
TCustomControl
解决方案。
Here is the code using TSCROLLINFO: 这是使用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.
我重命名了问题的标题,并删除了令人困惑的已弃用的MSDN文章。
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
. 出于某种原因,当启用主题时(此处的测试显示在Vista及更高版本中),控件似乎依赖于
WM_VSCROLL
的Word大小滚动位置数据。
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). 首先,一个复制问题的简单项目,下面是一个拥有大约600,000个项目的所有者绘制虚拟(
lbVirtualOwnerDraw
)列表框(因为项目数据没有被缓存,所以不需要花一点时间填充该框)。 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. 要查看问题只是通过拇指跟踪滚动条,您将会注意到项目是如何包装的,从每个65536开始,如Arnaud在问题评论中所述。 And when you release the thumb, it will snap to an item in the top
High(Word)
. 当您松开拇指时,它将捕捉到顶部
High(Word)
。
Below workaround intercepts WM_VSCROLL
on the control and performs thumb and item positioning manually. 下面的解决方法拦截控件上的
WM_VSCROLL
并手动执行拇指和项目定位。 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;
For a custom log viewer I wrote, I use a TListView
in virtual mode, not a TListBox
. 对于我写的自定义日志查看器,我在虚拟模式下使用
TListView
,而不是TListBox
。 Works great, no 32K limits, no need to fiddle with SetScrollInfo()
at all. 效果很好,没有32K限制,根本不需要使用
SetScrollInfo()
。 Just set the Item.Count
and the rest is handled automatically. 只需设置
Item.Count
,其余部分自动处理。 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. 它甚至还有一个
OnDataHint
事件,可以通过让您只加载TListView
实际需要的数据来优化数据访问。 You don't get that with a TListBox
. 你没有用
TListBox
得到它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.