2013-07-24

WPF中的ScrollViewer+ListView捲動問題

WPF中提供了一個ScrollViewer的控制項,他是個容器,當裡面元件的內容超過可顯示的範圍時,可以出現捲軸讓我們捲動繼續看完裡面的內容。

通常這個都是放一些動態的內容像是文字或是檔案之類的,文字倒還好,如果放的是要讓使用者可點選的ListView,那麼就算捲軸出現了我們還是不能利用滑鼠滾輪捲動。

我們用底下範例來說明:




紅框是ScrollViewer的大小,內部我們放了一個StackPanel,這個控制項也是個容器,可以讓所有內層控制項做垂直或水平的單一方向堆疊排列。裡面再用Expander包ListView,讓使用者可選擇是否要顯示ListView內容。由於ListView內容超過了ScrollViewer的高度,所以出現了捲軸。但實際跑這個程式,會發現無法用滑鼠滾輪控制捲動。原因就在當我們在其上使用滑鼠滾輪捲動時,捲的其實是ListView,但ListView又全部都顯示,造成捲動無效(要是有設定ListView的高度,那麼ListView自己就會出現捲軸)。

可能會想,「那我們就設定ListView的高度就好啦」,但在某些時候我們可能會在StackPanel裡面放很多Expander,讓使用者自由選擇要看哪一個的內容,到時候出現一大堆捲軸眼睛都花了!

若不設定高度,有時一個Expander就佔滿ScrollViewer的高度了,要看下一個的內容不就無法用滑鼠滾輪來捲動了嗎?

請看下圖,複雜的情況:



網路上鮮少有人提到這個,好不容易才找到了解決方法。

作法就是改寫ListView的MouseWheelEvent事件處理,讓ListView在捲動的時候,其實捲的是上層的ScrollView。

首先我們先寫一個Handler來處理滑鼠滾輪:
private void MyMouseWheelH(object sender, RoutedEventArgs e) {
    MouseWheelEventArgs eargs = (MouseWheelEventArgs)e;
    double x = (double)eargs.Delta;
    double y = MyScrollViewer.VerticalOffset;
    MyScrollViewer.ScrollToVerticalOffset(y - x); //MyScrollViewer是物件的Name
}

接著加入ScrollViewer的Loaded事件處裡:
private void MyScrollViewer_Loaded(object sender, RoutedEventArgs e) {
    MyListView.AddHandler(MouseWheelEvent, new RoutedEventHandler(MyMouseWheelH), true);
}

將ListView的MouseWhellEvent指定給上面的MyMouseWhellH來處理,這樣就可讓ScrollViewer捲動囉~