Listview внутри scrollviewer предотвращает прокрутку scrollviewer

У меня есть прокрутка с парой списков в нем. Проблема заключается в том, что пользователь использует средний ролик мыши для прокрутки прокрутки, когда его мышь находится над списком. Список просмотра прокручивает свой внутренний просмотрщик вниз, а затем продолжает захват мыши, предотвращая прокрутку содержащего прокрутки.

Любые идеи о том, как справиться с этим?

3 ответа

Это происходит потому, что ListViews (ListBoxна самом деле) шаблон содержимого оборачивает свои элементы ScrollViewer само собой.

Самый простой способ - отключить его, опустив свой Template для внутренней части ListViewтот, который не создает ScrollViewer:

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

Кстати, то же самое происходит, если у вас есть ListView внутри ListView (это был мой случай).

ИМО, лучший способ справиться с этим сценарием - создать настраиваемый элемент управления:

     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
                });
            }
        }
    }

Вы пытались отключить ListView'sScrollBars?

<ListView ScrollViewer.HorizontalScrollBarVisibility="Disabled"
          ScrollViewer.VerticalScrollBarVisibility="Disabled" />

Вдохновленный некоторыми полезными ответами, у меня есть реализация, которая прокручивает предки ScrollViewers, когда внутренние (в том числе из ListView, ListBox, DataGrid) прокручиваются мимо их верха / низа.

Я применяю прикрепленное свойство ко всем ScrollViewers в App.xaml:

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

Прикрепленное свойство обнаруживает прокрутку сверху / снизу, и когда это происходит, вызывает событие колесика мыши в родительском элементе ScrollViewer. Маршрутизация событий передает его внешнему 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,
        };
}

Если вы поместите внутренний просмотр списка в просмотрщик прокрутки, прокрутка будет работать.

<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>
Другие вопросы по тегам