滚动视图中的列表视图阻止了滚动视图的滚动

31

我有一个包含几个列表框的滚动查看器。问题是,如果用户在鼠标悬停在列表视图上时使用中间鼠标滚轮来滚动滚动查看器,列表视图将滚动其内部滚动查看器到底部,然后继续捕获鼠标,从而阻止容器滚动查看器的滚动。

有什么解决办法吗?


我有同样的问题。这里有一个讨论:http://social.msdn.microsoft.com/Forums/en-IE/wpf/thread/b440b2cb-26e0-4115-9858-5679c4e45e0a 如果我解决了这个问题,我会告诉你的。 - Kos
5个回答

70

这是因为 ListView (实际上是 ListBox)的内容模板会自动使用 ScrollViewer 包装其项。

最简单的方法是通过放置自己的内部 ListView 模板来禁用它,即不创建 ScrollViewer 的模板:

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

顺便提一下,如果你在一个ListView内部放置了另一个ListView(这就是我的情况),同样会发生相同的问题。


这个解决方案对我非常有效,而且很容易实现。我尝试了其他建议的解决方案,但只有这个对我起作用(在 ScrollViewer 中使用 ListViewer)。 - Peter
我正需要这个。作为一名长期的Windows 10开发者,这个问题在WPF中让我感到意外。谢谢+1 :) - iam.Carrot
哈哈,谢谢!自2013年以来我就没碰过C#了,很高兴看到这个答案仍然相关! - Kos

7

我认为处理这种情况的最佳方法是创建自定义控件:

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

2
你的答案是最好的,因为其他答案会移除标题,为了解决这个问题,你需要从头开始实现ScrollViewer,请参考ref1帮助 - Al Banna Techno logy

6
你是否尝试禁用ListViewScrollBars
<ListView ScrollViewer.HorizontalScrollBarVisibility="Disabled"
          ScrollViewer.VerticalScrollBarVisibility="Disabled" />

我做不到这一点,因为ListView中的项目可能比可见的更多。 - ConditionRacer
1
@Justin984 但是它们不是在另一个ScrollViewer中吗? - Rachel
啊,我本来想画一个小的ASCII图,但是太麻烦了。每个列表框都有多个条目。例如,列表框1可能有10个条目,需要滚动列表框才能查看。但是有多个列表框,加在一起太大了,所以我有一个外部的滚动视图器来滚动列表框以便查看。这样说清楚了吗? - ConditionRacer
@Justin984 或许你可以做一些事情,比如当 ListView 已经滚动到底部时,它停止响应向下滚动的事件?或者将这些事件传递给外部的 ScrollViewer - Rachel

5
受到一些有用的回答的启发(如此处此处此处),我有一个实现方案,当内部滚动组件(包括ListView、ListBox和DataGrid)滚动到顶部或底部时,它会滚动祖先级别的ScrollViewer。
我在App.xaml中通过附加属性将其应用于所有ScrollViewers:
<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,
        };
}

这对我有用,但我不得不使用TargetType ="{x: Type ScrollViewer}"而不是TargetType="ScrollViewer"。 - cnhe

0
如果将内部的Listview套入一个ScrollViewer中,则滚动将起作用。
<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>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接