複数のリストボックスのスクロールをスムーズに連動するには?

解決


ParOn  2003-08-23 05:57:13  No: 4456

3つあるListBoxのスクロール(TopIndexの増減)を同時に行おうと
スクロールバーのPosionを使って下記のようにしました。

ListBox1.TopIndex := ScrollBar1.Position;
ListBox2.TopIndex := ScrollBar1.Position;
ListBox3.TopIndex := ScrollBar1.Position;

(イベントはスクロールバーのOnChangeです)

ですが、1回目のスクロールの際
ListBox1からListBox3まで順番にカタンカタンカタンといった感じに
スクロールしてしまいます。

2回目以降のスクロールでは3つとも同時にスクロールしてくれるのですが
どうしても1回目だけが同時にスクロールしてくれなくて困っています。

あと上ではスクロールバーを使っていますが
ListBoxに表示されるスクロールバーを使って同じことができないでしょうか?
ListBoxでのスクロールバーのOnChangeに当たるイベントが分かりません。

宜しくお願いいたします。


にしの  2003-08-23 06:32:03  No: 4457

ちょっと裏技的ですが、こんな方法もあります。

procedure TForm1.ScrollBar1Change(Sender: TObject);
var
  Value: integer;
begin
  Value := ScrollBar1.Position;
  // 描画の禁止
  ListBox1.Perform(WM_SETREDRAW, 0, 0);
  ListBox2.Perform(WM_SETREDRAW, 0, 0);
  ListBox3.Perform(WM_SETREDRAW, 0, 0);
  SendMessage(ListBox1.Handle, LB_SETTOPINDEX, Value, 0);
  SendMessage(ListBox2.Handle, LB_SETTOPINDEX, Value, 0);
  SendMessage(ListBox3.Handle, LB_SETTOPINDEX, Value, 0);
  // 描画の許可(まだ描画しない)
  ListBox3.Perform(WM_SETREDRAW, 1, 0);
  ListBox2.Perform(WM_SETREDRAW, 1, 0);
  ListBox1.Perform(WM_SETREDRAW, 1, 0);
  // 再描画
  ListBox3.Invalidate;
  ListBox2.Invalidate;
  ListBox1.Invalidate;
end;


にしの  2003-08-23 06:34:58  No: 4458

失敗です。
スクロールバーまで再描画していませんでした。
# 最後までスクロールしてもスクロールバーは一番上のまま(実際は一番下)でした。
もうちっと調べます。


にしの  2003-08-23 07:21:52  No: 4459

少しだけ改良。
何も考えずに作ったので、もっと簡単な方法があるかもしれません。

ScrollBarの更新部分で、もしかしたらTopIndexと同じ値ではダメかもしれません。
その場合は、GetScrollInfoで範囲を取得し、正しい値を入れてやってください。

procedure TForm1.ScrollBar1Change(Sender: TObject);
var
  Value: integer;
begin
  Value := ScrollBar1.Position;
  // 描画の禁止
  ListBox1.Perform(WM_SETREDRAW, 0, 0);
  ListBox2.Perform(WM_SETREDRAW, 0, 0);
  ListBox3.Perform(WM_SETREDRAW, 0, 0);
  //値の設定
  SendMessage(ListBox1.Handle, LB_SETTOPINDEX, Value, 0);
  SendMessage(ListBox2.Handle, LB_SETTOPINDEX, Value, 0);
  SendMessage(ListBox3.Handle, LB_SETTOPINDEX, Value, 0);
  // 描画の許可(まだ描画しない)
  ListBox3.Perform(WM_SETREDRAW, 1, 0);
  ListBox2.Perform(WM_SETREDRAW, 1, 0);
  ListBox1.Perform(WM_SETREDRAW, 1, 0);
  // 再描画(スクロール部分は再描画されない)
  InvalidateRect(ListBox1.Handle, nil, TRUE);
  InvalidateRect(ListBox2.Handle, nil, TRUE);
  InvalidateRect(ListBox3.Handle, nil, TRUE);
  // スクロールバーを更新
  SetScrollPos(ListBox1.Handle, SB_VERT, Value, TRUE);
  SetScrollPos(ListBox2.Handle, SB_VERT, Value, TRUE);
  SetScrollPos(ListBox3.Handle, SB_VERT, Value, TRUE);
end;


ParOn  2003-08-23 07:50:41  No: 4460

おーキレイにスクロールしてます!
このような方法があるんですね。

正直なところよく理解できてませんが・・・

今から、自分なりに消化して理解しようと思います。
やっぱりプログラム組むのは奥が深くて面白いです。

にしのさん、本当にありがとうございました。


Halbow  URL  2003-08-24 11:41:57  No: 4461

Halbow です。

>あと上ではスクロールバーを使っていますが
>ListBoxに表示されるスクロールバーを使って同じことができないでしょうか?

ローカルフックで解決できます。新規で以下のコードを試してみてください。
3つの ListBox のどのスクロールバーを動かしても、他の二つが追随します。
矢印キーによるスクロールには対応していません。(できますが)

type
  TForm1 = class(TForm)
    ListBox1: TListBox;
    ListBox2: TListBox;
    ListBox3: TListBox;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private 宣言 }
  public
    procedure ProcessMessage(hWindow:HWND;Msg:UINT;wParam:WPARAM;lParam:LPARAM);
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

var
  Hook: HHOOK;

function CallWndRetProc(code:integer;wparam:WPARAM;lparam:LPARAM):LRESULT;stdcall;
var
  pCWPR:PCWPRetStruct;
begin
  if (code < 0) then begin
    result := CallNextHookEx(Hook,code,wparam,lparam);
    exit;
  end;

  if (Code = HC_ACTION) then begin
    pCWPR := PCWPRetStruct(lParam);
    Form1.ProcessMessage(pCWPR^.hwnd,pCWPR^.message,pCWPR^.wParam,pCWPR^.lParam);
  end;

  result := CallNextHookEx(Hook,code,wparam,lparam);
end;

procedure TForm1.ProcessMessage(hWindow:HWND;Msg:UINT;wParam:WPARAM;lParam:LPARAM);
begin
  if Msg <> WM_VSCROLL then exit;

  if hWindow = ListBox1.Handle then begin
    ListBox2.Perform(Msg,wParam,lparam);
    ListBox3.Perform(Msg,wParam,lparam);
  end else
  if hWindow = ListBox2.Handle then begin
    ListBox1.Perform(Msg,wParam,lparam);
    ListBox3.Perform(Msg,wParam,lparam);
  end else
  if hWindow = ListBox3.Handle then begin
    ListBox1.Perform(Msg,wParam,lparam);
    ListBox2.Perform(Msg,wParam,lparam);
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Hook := SetWindowsHookEx(WH_CALLWNDPROCRET,CallWndRetProc,0,GetCurrentThreadID);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  UnHookWindowsHookEx(Hook);
end;


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加