テーマが有効になっていない限り、リストボックス コントロールの既定のウィンドウ プロシージャはサム トラッキングを適切に処理するため、以下は OS の動作に問題がある場合の回避策と見なす必要があります。何らかの理由で、テーマが有効になっている場合 (ここでのテストは 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が説明したように、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;