9

in virtual modeを使用してログ ビューアーをTListBox実装しました。

それは(私が書いたすべてのコードに対して)正常に動作し、期待どおりにコンテンツを表示します(水平スクロールバーを簡単に追加しました)が、垂直スクロールバーのある種の制限に達したと思います.

つまり、垂直バーを上から下にスクロールすると、コンテンツはリストの最後までスクロールされず、ある程度までしかスクロールされません。

この制限を取り除く可能性を知っていますか? で試しましSetScrollInfoたが、制限がスクロールバーではなく、TListBoxそれ自体にあるように聞こえるため、機能しませんでした。

専用を作成するソリューションを知っています。TCustomControlこの場合、 はSetScrollInfo期待どおりに機能します。しかし、まだ使用するソリューション/トリックについて知っている人はいますTListBoxか?

編集:明確にするために-(サードパーティの)コンポーネントソリューションは求めていませんが、TListBoxこの制限をオーバーライドするために標準に送信する低レベルのGDIメッセージがあるかどうかを知りたい. 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

テーマが有効になっていない限り、リストボックス コントロールの既定のウィンドウ プロシージャはサム トラッキングを適切に処理するため、以下は 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;
于 2011-08-22T22:14:26.830 に答える
1

私が作成したカスタム ログ ビューアの場合、TListView仮想モードではTListBox. うまく機能し、32K の制限もなく、いじる必要もまったくありませんSetScrollInfo()。を設定するだけItem.Countで、残りは自動的に処理されます。実際に必要なOnDataHintデータのみをロードできるようにすることで、データ アクセスを最適化するために使用できるイベントもあります。TListViewではそれは得られませんTListBox

于 2011-08-22T22:49:13.323 に答える