WP TIPS に戻る

ListBox をスクロールさせる

サンプルプロジェクト Listbox_scroll.zip

ListBox のスクロール部分は ScrollViewer で実装されており、この ScrollViewer を取り出して、使用することで ListBox に表示されている項目をスクロールさせることが出来ます。

ListBox の ScrollViewer を取り出す

取り出し方は簡単です。ListBox の最初の子エレメントが ScrollViewer になっているので、VisualTreeHelper.GetChild メソッドで指定すれば取得できます。

private ScrollViewer GetScrollViewer(ListBox lb)
{
   return (ScrollViewer)VisualTreeHelper.GetChild(lb, 0);
}

ListBox をスクロールさせる

上記の方法で取得した ScrollViewer の ScrollToVerticalOffset メソッドを使用することで、上下にスクロールさせることが出来ます。( ScrollToHorizontalOffset なら左右 )

ScrollToVerticalOffset メソッドに指定する値ですが、通常の ScrollViewer であればピクセルを指定するので 100 を指定すると、左上から 100px の所にスクロールします。
しかし ListBox の ScrollViewer での指定は、表示したい項目のインデックスになります。

たとえば上から10番目の項目を ListBox の先頭に表示させる場合は、9 を指定します。
さらに ScrollToVerticalOffset の値は、double の小数点を指定することが出来、9.5 を指定すると 10番目の項目が半分だけ表示されます(上半分はスクロールしていて隠れる)。

つまり 1つの項目が 1 に相当し、0.1 ずつ足していくと 項目の高さ 10% ずつ下側にスクロール させることが出来ます。逆に 0.1 ずつ引いていくと上側にスクロールします。

DispatcherTimer を使用し 0.1 ずつ変化させると、自動的にスクロールする効果を演出することが出来ます。

private void down_button_Click(object sender, System.EventArgs e)
{
   // 現在位置を取得 (表示されている項目のインデックスと等価)
   int currentIndex = (int)Math.Floor(this.sv.VerticalOffset);

   this.Start(currentIndex + 5);
}

IDisposable q;

private void Start(double to)
{
   // 既に動いていたら停止
   if (this.q != null)
   {
       this.q.Dispose();
       this.q = null;
   }

   // 現在位置を取得 (表示されている項目のインデックスと等価)
   double from = (int)Math.Floor(this.sv.VerticalOffset);
   
   // タイマで 10ms ごとに、項目の高さ 10% ずつスクロールさせる
   double offset = 0.1;
   bool isDown = from < to;
   DispatcherTimer timer = new DispatcherTimer();
   timer.Interval = TimeSpan.FromMilliseconds(10);

   System.Diagnostics.Debug.WriteLine(from + " " + to);
   
   this.q = Observable.FromEvent<EventArgs>(timer, "Tick")
       .Select(n =>
       {
           from = isDown ? from + offset : from - offset;
           return from;
       })
       .Subscribe(n =>
       {
           // 範囲を超えたら終了
           if (isDown && n > to)
               this.q.Dispose();
           else if (!isDown && n < to)
               this.q.Dispose();

           // スクロール
           this.sv.ScrollToVerticalOffset(n);
       });
   
   // スクロール開始
   timer.Start();
 }

スクロール時の注意

上記のように ScrollToVerticalOffset メソッドに 0.1 ずつ値を加算(または減算)することで、なめらかなスクロールを行うことが出来ます。
ただし注意しなければならないのは、0.1 というのは項目の高さの 10% である と言うことです。

たとえば ListBox 最初の項目が 100px の高さの場合 0.1 = 10% = 10px ですが、2番目の項目が 200px の場合 0.1 = 10% = 20px になると言うことです。
高さが違う項目が表示されている場合に 0.1 ずつ足してスクロールさせると、高さの低い項目はゆっくりスクロールし、高い項目は早くスクロールすることになります。

この場合は、各項目の高さを取得し逆算した上で、ScrollToVerticalOffset メソッドに値を設定する必要があります。