2つのリストビューを連動させるには

解決


すずむし  2008-09-21 11:32:11  No: 69029

MFC  .NET2008です。

2つのリストビューに、同じ大きな表が入って、縦横にスクロールバーが出ている状態で、その2つのリストビューを、スクロールバーで連動させる方法が分かりません。

リストビューAのスライダーを動かした時に、リストビューBのスライダーも同じ位置まで移動させるのが、目的です。

OnHScrollやOnVScrollで解決しそうに思ったですが、
たとえば、

void C***View::OnHScroll(UINT nSBCode, UINT nPos, pCScrollBar* ScrollBar)
    if ( ( CSliderCtrl* )pScrollBar == &m_list1 ) { 
        int p = m_list1.GetPos();
    }
}

とすると、メンバかみ合わず、どのリストビューが選択されているのか、取得できません。

初心者で、行き詰ってしまいました。

アドバイスをお願いいたします。


そだ  2008-09-21 13:44:15  No: 69030

リストビューに付属しているスクロールバーからみたダイアログボックスて親(リストビュー)の親、つまりおじいちゃんなわけでOnHScrollは飛んでこないような・・・
onScrollで処理するならリストビューのonScrollですれば動いてくれるんじゃないでしょうか。


すずむし  2008-09-21 16:12:28  No: 69031

ありがとうございます。

ご指摘の通り、リストビューをサブクラス化したら、OnHScrollが呼ばれました。

次の問題なのですが、どのリストビューのスライダーを動かしたのかが、取得できません。

if ( ( CSliderCtrl* )pScrollBar == &m_list1 ) { 
}

とすると、「'm_list1' : 定義されていない識別子です。」
というエラーになります。
考えてみれば、当然のことですが、m_list1の中のスクロールバーを、どう表現したらいいのか分かりません。
できれば、複数のリストビューを一つのサブクラスで済ませたいのですが、無理でしょうか。

アドバイスをお願いいたします。


wclrp ( 'o')  2008-09-22 06:45:38  No: 69032

そもそもリストビューで自分のスクロールメッセージを受け取るなら
ifいらないでしょ。
別のリストビューのスクロールメッセージを受け取るはずないから。

リストビューに複数のスクロールやスライダーなどのコントロールを
子ウィンドウとして貼り付けているならifが必要だけど。


そだ  2008-09-22 09:19:50  No: 69033

サブクラス化ついでにお互いにダイアログ上でアドレスを渡すメソッドを実装して、
一方のリストビューがもう一方のリストビューのアドレスを持てるようにしてやればいいんじゃないでしょうか。
C****View::OnInitDialog() {
...
m_list1->setOtherView(&m_list2);
m_list2->setOtherView(&m_list1);
}
あとは、OnHScrollでpCScrollBar* ScrollBarから自身のビューの位置を取得して、もう一方のビューをスライドさせてやれば・・・。


すずむし  2008-09-22 16:56:34  No: 69034

ありがとうございます。

「複数のスクロールやスライダーなどのコントロールを子ウィンドウとして貼り付け」られたらいいなあ、と思ったのですが、分かりませんし、質問の趣旨ではないので、送る側のリストビューでけサブクラスにしました。

void CmyListCtrl::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
    //---------------------------------------------■ここから
    CFrameWnd* pFrame = (CFrameWnd*)AfxGetMainWnd();
    C***View* pView = (C***View*)pFrame->GetActiveView();
    pView->UpdateData(TRUE);

    pView->m_list2.Scroll( CSize( nPos, 0 ) ); 
    //---------------------------------------------■ここまで 
    CListCtrl::OnHScroll(nSBCode, nPos, pScrollBar);
}

としたところ、動くことは動いたのですが、位置がずれてしまいます。

ブレークポイントを置くと、nPosには、ピクセル単位の値が入っているようなので、Scrollの引数の問題だと思うのですが、分かりません。
MSDNでは、「水平および垂直方向へのスクロール量をピクセル単位で指定する CSize オブジェクト」となっています。

また、nPosの型は、UINT なので、送る側でスライダーを戻しても、送られる側のスライダーは前に進んでしまいます。
MSDNでは、「int型にキャストしてください」となっていますが、この場合、どのようにしたらよいのでしょうか。

よろしくお願いします。


そだ  2008-09-23 01:23:01  No: 69035

UINT->intのキャストは static_cast  かな。
static_cast<int>(nPos)

ただし、ずれるのはnPosが絶対位置を表すのに対し、
Scrollが相対的な移動量を指定する関数だからでは?

絶対位置を指定するCWnd::SetScrollPosでどうでしょうか。
CListCtrlでもCWndを継承しているから使えるはず。


すずむし  2008-09-23 16:08:41  No: 69036

そださん、wclrp ( 'o')さん、ありがとうございます。

void CmyListCtrl::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
  CFrameWnd* pFrame = (CFrameWnd*)AfxGetMainWnd();
  C***View* pView = (C***View*)pFrame->GetActiveView();
  pView->UpdateData(TRUE);

  pView->m_list2.SetScrollPos( SB_HORZ, nPos, TRUE ); 

  CListCtrl::OnHScroll(nSBCode, nPos, pScrollBar);
}

と、してみました。

まだ、2点、問題が残っています。

1.スクロールバーは動くのですが、肝心の表が動きません。
Scroll以外に、ピクセル単位で表を動かす方法が分かりません。

2.動かす側のスクロールバーからマウスポインタを離したとたんに、動かされる側のスクロールバーが、左端に戻ってしまいます。
動かす側のスクロールバーを掴みなおすと、動かされる側のスクロールバーは、あるべき位置に戻ります。
MSDNを見ると、戻り値には直近の値が入っているようですが、どう活かしたらいいのか分かりません。

分からないことばかりですが、アドバイスを頂けませんでしょうか。


そだ  2008-09-23 16:45:43  No: 69037

SetScrollPosだけだとツマミが動くだけでしたか。

CListCtrl::OnHScrollを呼び出す関数を作ってSetScrollPosの後に実行してもできそうだけどちょっと怖いような。

相対でやってるとずれそうな気がしてたけど絶対位置を元に毎回求めてやれば大丈夫かな。SetScrollPosの代わりにこういうコードでどうでしょ。

int diff = static_cast<int>(nPos) - static_cast<int>(pView->m_list2.GetScrollPos(SB_HORZ)); 
if( diff != 0 ) {
    pView->m_list2.Scroll( CSize( diff, 0 ) ); 
}

ScrollってOnHScrollを呼び出さないのかな。一応diffが0のときははじくように書いたけどちょっと怖いかも。


wclrp ( 'o')  2008-09-23 19:13:08  No: 69038

nSBCodeの値によってドラッグ開始や終了などを知らせる。
nSBCodeの値によってnPosを使わないものがある。
一度調べて把握するべきです。

具体的にどうすれば連動できるかは知りません。
誰かすでに同じことやっていそうではありますね。
検索して見つかればいいですけど。


すずむし  2008-09-24 13:52:48  No: 69039

そださん、wclrp ( 'o')さん、ありがとうございます。

そださんのコードで成功しました。
ウインドウの位置を含め、繰り返し動かせてみましたが、その範囲では、ずれは起こりませんでした。

たいへんありがとうございました。


すずむし  2008-09-24 15:31:07  No: 69040

たびたび、申し訳ありません。

解決したと思ったのですが、縦方向も連動させようとすると、正確に動きません。
置き換えたコードは下記です。

int diff = static_cast<int>(nPos) - static_cast<int>(pView->m_list2.GetScrollPos(SB_VERT)); 
if( diff != 0 ) {
    pView->m_list2.Scroll( CSize( 0, diff ) ); 
}

動かす側のスクロールバーを1/3くらい動かせたところから、動かされる側のスクロールバーが動き始め、1/2くらいで止まります。
SetScrollRangeではかえっておかしくなるので、選択範囲の問題ではないようです。
何が問題か分かりません。

申し訳ありませんが、アドバイスをお願いいたします。


そだ  2008-09-24 17:59:50  No: 69041

リストコントロールの行数か、
スクロールバーの範囲がそれぞれ違っていたりしませんか?


すずむし  2008-09-24 20:30:56  No: 69042

そださん、ありがとうございます。

nPosは、ピクセル単位ではないのでしょうか。。。
選択範囲も、間違っているとしても、どんな値を入れたらいいのか分かりません。


そだ  2008-09-24 22:48:27  No: 69043

wclrp ( 'o')氏もおっしゃってましたが
スクロール要求によってnPosが変わるみたいなので
マウスによって移動させられた側のツマミ位置も
GetScrollPosでとってみてはいかがでしょう。


すずむし  2008-09-26 16:42:06  No: 69044

そださん、ありがとうございます。

OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)で、
動かす側の、サブクラス化したリストビューのGetScrollLimitには、総行数−1の値が入っているようです。
今のところ、動される側のリストビューのGetScrollLimitの値は、意味不明です。

でも、それならそれでやり方があるかもしれないので、もう少し工夫してみます。


すずむし  2008-09-28 15:32:07  No: 69045

そださん、wclrp ('o')さん、ありがとうございます。

目的通り動きました。

垂直スクロールでは、nPosに入る値は、ピクセルではなく、行単位の増分、減分になるようです。
動かされる側のスクロールバーに意味不明に見える値が入ったのは、EnsureVisibleの場合と同じく、スクロールバーが折り返して、下からスクロールしたためだと思います。

すいすい動きます。

たいへんありがとうございました。


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

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






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