1. 程式人生 > >[WPF自定義控制元件庫] 關於ScrollViewr和滾動輪劫持(scroll-wheel-hijack)

[WPF自定義控制元件庫] 關於ScrollViewr和滾動輪劫持(scroll-wheel-hijack)

1. 什麼是滾動輪劫持

這篇文章介紹一個很簡單的繼承自ScrollViewer的控制元件:

public class ExtendedScrollViewer : ScrollViewer
{
    protected override void OnMouseWheel(MouseWheelEventArgs e)
    {
        if (ViewportHeight + VerticalOffset >= ExtentHeight && e.Delta <= 0)
            return;

        if (VerticalOffset == 0 && e.Delta >= 0)
            return;

        base.OnMouseWheel(e);
    }
}

所有程式碼就這麼多,這個ExtendedScrollViewer 只是用來解決滾動輪劫持(scroll-wheel-hijack)的問題。所謂的滾動輪劫持,簡單來說即是在一個可以滾動的頁面使用滑鼠滾輪滾動頁面的過程中滑鼠進入某個可以滾動的子元素導致只在這個子元素中滾動而整個頁面想滾滾不動了。

具體看看這個例子:

這個情況相信很多人都遇到過,滾輪被“劫持”後索性去拖動滾動條。有次我遇到個內嵌了很多ScrollViewer的長頁面,使用起來真的很惱人,所以我使用ExtendedScrollViewer 解決了這個問題。當然還有另外很多種情況的滾動輪劫持,也有很多解決方案,這篇文章只介紹我遇到的情況和我的解決方案。

2. 實現

在WPF中要禁止ScrollViewer捕獲滑鼠滾動時間,可以重寫OnMouseWheel成一個空的方法:

protected override void OnMouseWheel(MouseWheelEventArgs e)
{
}

OnMouseWheel方法用於響應滑鼠滾輪的事件,將它過載成空方法即不再處理滑鼠滾利事件。注意在這種情況下不可以使用e.Handled = true,因為我們的目標是讓外層的ScrollViewer可以接收到滑鼠滾輪事件,所以不能更改MouseWheelEventArgs 的Handled。

當然我們不滿足於無腦禁用滑鼠滾輪,我們應該更智慧些,先讓ScrollViewer滾到底,再交由外層的ScrollViewer滾下去。這裡面用到幾個屬性:

MouseWheelEventArgs中的Delta表示滑鼠滾輪的變更量,當這個值為正數時表示滾輪向上。

ExtentHeight,獲取ScrollViewer內容的實際高度。

ViewportHeight,獲取當前可視區域的高度。

VerticalOffset,包含滾動內容對應於頁首的垂直偏移量的值,有效值介於 0 與 ExtentHeight 減去 ViewportHeight 所得的數值之間。

熟悉了上面幾個屬性的作用後我們可以更好地控制滑鼠滾輪的行為,當滑鼠向上滾動時,判斷現在是否已經滾到頂了,如果是就不處理滑鼠滾輪事件:

if (VerticalOffset == 0 && e.Delta >= 0)
    return;

而當滑鼠向下滾動時,需要根據ViewportHeightVerticalOffsetExtentHeight判斷當前是否已經滾動到底,如果是就不處理滑鼠滾輪事件:

if (ViewportHeight + VerticalOffset >= ExtentHeight && e.Delta <= 0)
    return;

3. 其他ScrollViewer方案

ScrollViewer還有很多中玩法,但我工作中不常用到所以就沒做。如果覺得不滿足還可以參考HandyControl的ScrollViewer,它直接提供了一個CanMouseWheel屬性用於控制是否響應滑鼠滾輪,另外還支援了滾動等功能。

4. 參考

ScrollViewer.OnMouseWheel(MouseWheelEventArgs) Method (System.Windows.Controls) Microsoft Docs

MouseWheelEventArgs.Delta Property (System.Windows.Input) Microsoft Docs

ScrollViewer.ExtentHeight Property (System.Windows.Controls) Microsoft Docs

ScrollViewer.ViewportHeight Property (System.Windows.Controls) Microsoft Docs

ScrollViewer.VerticalOffset Property (System.Windows.Controls) Microsoft Docs

5. 原始碼

ExtendedScrollViewer.cs at mas