Listview inside of scrollviewer prevents scrollviewer scroll

后端 未结 5 1901
佛祖请我去吃肉
佛祖请我去吃肉 2021-01-01 10:00

I have a scrollviewer with a couple listboxes in it. The problem is if a user uses the middle mouse roller to scroll the scrollviewer while their mouse is over a listview. T

5条回答
  •  没有蜡笔的小新
    2021-01-01 10:42

    Inspired by some helpful answers, I have an implementation that scrolls ancestor ScrollViewers when inner ones (including from ListView, ListBox, DataGrid) scroll past their top/bottom.

    I apply an attached property to all ScrollViewers in App.xaml:

    
    
    

    The attached property detects scrolling past top/bottom, and when that happens raises a mouse wheel event on the ScrollViewer's parent. Event routing gets it to the outer ScrollViewer:

    public static class ScrollViewerHelper
    {
        // Attached property boilerplate
        public static bool GetFixMouseWheel(ScrollViewer scrollViewer) => (bool)scrollViewer?.GetValue(FixMouseWheelProperty);
        public static void SetFixMouseWheel(ScrollViewer scrollViewer, bool value) => scrollViewer?.SetValue(FixMouseWheelProperty, value);
        public static readonly DependencyProperty FixMouseWheelProperty =
            DependencyProperty.RegisterAttached("FixMouseWheel", typeof(bool), typeof(ScrollViewerHelper),
                new PropertyMetadata(OnFixMouseWheelChanged));
        // End attached property boilerplate
    
        static void OnFixMouseWheelChanged(DependencyObject d,
                                           DependencyPropertyChangedEventArgs e)
        {
            var scrollViewer = d as ScrollViewer;
            if (scrollViewer == null) return;
    
            scrollViewer.PreviewMouseWheel += (s2, e2) =>
            {
                var parent = scrollViewer.Parent as UIElement;
                bool hitTopOrBottom = HitTopOrBottom(e2.Delta, scrollViewer);
                if (parent is null || !hitTopOrBottom) return;
    
                var argsCopy = Copy(e2);
                parent.RaiseEvent(argsCopy);
            };
        }
    
        static bool HitTopOrBottom(double delta, ScrollViewer scrollViewer)
        {
            var contentVerticalOffset = scrollViewer.ContentVerticalOffset;
    
            var atTop = contentVerticalOffset == 0;
            var movedUp = delta > 0;
            var hitTop = atTop && movedUp;
    
            var atBottom =
                contentVerticalOffset == scrollViewer.ScrollableHeight;
            var movedDown = delta < 0;
            var hitBottom = atBottom && movedDown;
    
            return hitTop || hitBottom;
        }
    
        static MouseWheelEventArgs Copy(MouseWheelEventArgs e)
            => new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta)
            {
                RoutedEvent = UIElement.MouseWheelEvent,
                Source = e.Source,
            };
    }
    

提交回复
热议问题