9

我已经使用in virtual mode实现了一个日志查看器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 文章,这些文章令人困惑。

4

2 回答 2

10

下面可能应该被视为有缺陷的操作系统行为的解决方法,因为除非启用主题,否则列表框控件的默认窗口过程可以很好地处理拇指跟踪。出于某种原因,启用主题时(此处测试显示 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;


要查看问题,只需用拇指跟踪滚动条,您会注意到项目是如何从头开始包装的,正如 Arnaud 在问题评论中所描述的那样。当你松开拇指时,它会吸附到顶部的一个项目上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;
于 2011-08-22T22:14:26.830 回答
1

对于我编写的自定义日志查看器,我TListView在虚拟模式下使用 a,而不是TListBox. 效果很好,没有 32K 限制,根本不需要摆弄SetScrollInfo()。只需设置Item.Count,其余的将自动处理。它甚至有一个OnDataHint事件,可用于通过让您仅加载TListView实际需要的数据来优化数据访问。你不会用TListBox.

于 2011-08-22T22:49:13.323 回答