如何在ListBox中禁用ScrollViewer?

18

我有一个ListBox,它有内部的ScrollViewer,所以我可以用鼠标滚轮滚动ListBox内容。在不设置包含另一个ListBox的项模板时,一切正常(实际上我有4个嵌套的ListBox)。问题在于内部ListBox的ScrollViewer会夺取鼠标滚轮事件。是否有简单的方法来防止这种行为?


我有一个带有以下ItemContainerStyle的ListBox:

<Style x:Key="ListBoxItemStyle" TargetType="{x:Type ListBoxItem}">
    <Setter Property="BorderBrush" Value="Black"/>
     ... 
</Style>
<ListBox ItemContainerStyle="{StaticResource ListBoxItemStyle}" />

我该如何在类似这样的资源中为ItemContainer的项目边框设置样式?据我所知,ContentPresenter是ItemsControl的项目容器。但它没有边框,所以我无法对其进行样式设置。

6个回答

62

通过更改ListBox的控件模板,可以将其中的ScrollViewer移除,使其变得简单易用:

<ListBox>
    <ListBox.Template>
        <ControlTemplate>
            <ItemsPresenter />
        </ControlTemplate>
    </ListBox.Template>
    ...
</ListBox>

然而,我对嵌套ListBoxes的价值提出了质疑。请记住每个ListBox都是一个选择器,并且有一个所选项目的概念。在所选项目内部再次嵌套所选项目真的有意义吗?

我建议将"内部"的ListBoxes更改为简单的ItemsControls,以便嵌套的列表不能有所选项目。这将使用户体验更加简单。您可能仍需要以相同的方式重新模板化内部的ItemsControls以删除滚动条,但至少用户不会因为弄不清哪个项目被"选中"而感到困惑。


1
真的有必要在所选项目内部再次选择一个所选项目吗?是的。否则你怎么能从列表内嵌套的列表中选择一个项目呢? - wotanii
1
这也会从列表框中删除很多其他内容,例如下拉事件。 - wotanii
@wotanii,你找到了一个不会删除drop事件的解决方案吗?(我正在使用触摸滚动) - JP Garza
从列表中选择一个嵌套了另一个列表的项目?这不是树视图的作用吗? - Phil Rogers

15

您可以通过在XAML中捕获滚动事件来禁用窃取滚动事件:

<ListBox PreviewMouseWheel="ScrollViewer_PreviewMouseWheel">

并在代码后端重新发布:

private void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
    {
        if (sender is ListBox && !e.Handled)
        {
            e.Handled = true;
            var eventArg = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta);
            eventArg.RoutedEvent = UIElement.MouseWheelEvent;
            eventArg.Source = sender;
            var parent = ((Control)sender).Parent as UIElement;
            parent.RaiseEvent(eventArg);
        }
    }

这个解决方案确切地是针对 ListBox 的,但它也帮助了我解决 ListView 的问题。

我在这里找到了这个解决方案:

https://social.msdn.microsoft.com/Forums/vstudio/en-US/3a3bb6b0-e088-494d-8ef2-60814415fd89/swallowing-mouse-scroll?forum=wpf


谢谢!我已经找了很久了。它起作用了! - Sebastian Berge
如果你使用触摸滚动而不是鼠标滚轮,你会如何解决这个问题? - JP Garza
我没有现成的解决方案,也无法测试它,但我会开始寻找类似的触摸屏相关事件,并重新发布它们,就像我所重新发布的解决方案一样。 - tequilacat

4

我喜欢为这种类型的事情创建一个行为。

xmlns:bhv="http://schemas.microsoft.com/xaml/behaviors"

(注:该段为HTML代码,无法翻译成中文,保留原样。)
<ListView ItemsSource="{Binding Items}">
    <bhv:Interaction.Behaviors>
        <bhvs:NoScrollingBehavior/>
    </bhv:Interaction.Behaviors>
</ListView>

行为本身。
public class NoScrollingBehavior : Behavior<UIElement>
{
    public NoScrollingBehavior()
    { }

    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.PreviewMouseWheel += PreviewMouseWheel;
    }

    protected override void OnDetaching()
    {
        AssociatedObject.PreviewMouseWheel -= PreviewMouseWheel;
        base.OnDetaching();
    }

    private void PreviewMouseWheel(object sender, MouseWheelEventArgs e)
    {
        e.Handled = true;
        var eventArg = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta);
        eventArg.RoutedEvent = UIElement.MouseWheelEvent;
        eventArg.Source = sender;
        var parent = ((Control)sender).Parent as UIElement;
        parent.RaiseEvent(eventArg);
    }
}

谢谢,我正在寻找使用MVVM的解决方案! - Keytrap

1

抱歉打扰了这么旧的帖子。实际上,您可以使用ScrollViewer的附加属性来禁用ScrollViewer。

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

8
这个句子似乎仍然让轮胎旋转事件遭到了窃取。 - wotanii

0
这是一个使用DependencyProperty的变体,如果你不喜欢行为方式的话。
public class IgnoreScrollingBehavior
{
    public static readonly DependencyProperty IgnoreScrollingProperty =
        DependencyProperty.RegisterAttached("IgnoreScrolling", typeof(bool),
            typeof(IgnoreScrollingBehavior), new UIPropertyMetadata(false, OnIgnoreScrollingChanged));

    public static bool GetIgnoreScrolling(UIElement uIElement)
    {
        return (bool)uIElement.GetValue(IgnoreScrollingProperty);
    }

    public static void SetIgnoreScrolling(UIElement uIElement, bool value)
    {
        uIElement.SetValue(IgnoreScrollingProperty, value);
    }

    private static void OnIgnoreScrollingChanged(DependencyObject depOpj, DependencyPropertyChangedEventArgs e)
    {
        if (depOpj is not UIElement item)
        {
            return;
        }

        if (e.NewValue is bool boolean)
        {
            if (boolean)
            {
                item.PreviewMouseWheel += OnPreviewMouseWheel;
            }
            else
            {
                item.PreviewMouseWheel -= OnPreviewMouseWheel;
            }
        }
    }

    private static void OnPreviewMouseWheel(object sender, MouseWheelEventArgs e)
    {
        e.Handled = true;
        MouseWheelEventArgs eventArg = new(e.MouseDevice, e.Timestamp, e.Delta)
        {
            RoutedEvent = UIElement.MouseWheelEvent,
            Source = sender
        };
        UIElement parent = ((Control)sender).Parent as UIElement;
        parent.RaiseEvent(eventArg);
    }
}

这是它的使用方法

<Listbox b:IgnoreScrollingBehavior.IgnoreScrolling="True".../>

-1

你可以使用这个!没有轮子被偷。

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

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