Listview inside of scrollviewer prevents scrollviewer scroll

后端 未结 5 1863
佛祖请我去吃肉
佛祖请我去吃肉 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:

    <Style TargetType="ScrollViewer" BasedOn="{StaticResource {x:Type ScrollViewer}}">
        <Setter Property="local:ScrollViewerHelper.FixMouseWheel" Value="True" />
    </Style>
    
    

    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,
            };
    }
    
    0 讨论(0)
  • 2021-01-01 10:42

    If you wrap the inner listview in a scrollviewer then the scrolling will work.

    <ListView ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
        <ListView.ItemTemplate>
            <DataTemplate>
                <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled">
                    <ListView>
                        <ListView.ItemTemplate>
                            <DataTemplate>
                            </DataTemplate>
                        </ListView.ItemTemplate>
                    </ListView>
                </ScrollViewer>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
    
    0 讨论(0)
  • 2021-01-01 10:45

    Did you try disabling the ListView's ScrollBars?

    <ListView ScrollViewer.HorizontalScrollBarVisibility="Disabled"
              ScrollViewer.VerticalScrollBarVisibility="Disabled" />
    
    0 讨论(0)
  • 2021-01-01 10:54

    IMO, the best way to handle this scenario is to create a custom control :

         class MyScrollViewer : ScrollViewer
         {
             protected override void OnPreviewMouseWheel(MouseWheelEventArgs e)
             {
                base.OnPreviewMouseWheel(e);
                if (!e.Handled)
                {
                    e.Handled = true;
                    this.RaiseEvent(new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta)
                    {
                        RoutedEvent = UIElement.MouseWheelEvent,
                        Source = this
                    });
                }
            }
        }
    
    0 讨论(0)
  • 2021-01-01 10:59

    That happens because the ListView's (ListBox's, actually) content template wraps its items with a ScrollViewer by itself.

    The simplest way is to disable it by dropping your own Template for the inside ListView, one that doesn't create a ScrollViewer:

        <ListView>
          <ListView.Template>
            <ControlTemplate>
              <ItemsPresenter></ItemsPresenter>
            </ControlTemplate>
          </ListView.Template>
          ...
        </ListView>
    

    BTW the same happens if you have a ListView inside a ListView (this was my case).

    0 讨论(0)
提交回复
热议问题