How to disable ScrollViewer in ListBox?
Asked Answered
O

6

18

I have a ListBox. It has internal ScrollViewer, so I can scroll ListBox content with mouse wheel. It works fine until I set item template that contains another ListBox (in fact, I have 4 nested ListBoxes =)). Problem is that internal ListBox's ScrollViewer steals wheeling event. Is there any easy way to prevent this behavior?


I had ListBox with ItemContainerStyle like this:

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

How can I set style for ItemContainer's item border in resources like this? As I know ContentPresenter is item container of ItemsControl. But it hasn't Border, so I can't style it.

Olympe answered 15/11, 2009 at 23:8 Comment(0)
R
62

You can remove the ScrollViewer from a ListBox by changing its control template to something much simpler:

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

However, I question the value of nesting ListBoxes. Remember that each ListBox is a Selector and has a concept of which item is "selected". Does it really make sense to have a selected item inside a selected item, inside a selected item?

I would suggest changing the "inner" ListBoxes to simple ItemsControls so that the nested lists can't have selected items. That would make for a much simpler user experience. You may still need to retemplate the inner ItemsControls in the same way to remove the scrollbars, but at least the user won't get confused about which item is "selected".

Repp answered 15/11, 2009 at 23:35 Comment(4)
Does it really make sense to have a selected item inside a selected item, inside a selected item? Yes. How else would you select an item from a list inside a list inside a list?Psychometry
this also removes a lot of other stuff from the listbox as well, e.g. drop-eventsPsychometry
@Psychometry did you find a solution that does not remove drop-events? (im using touch to scroll)Primordial
Selecting an item from a list inside a list inside a list ? Isn't that what Treeviews are for?Adopted
H
16

You can disable stealing scroll events by catching scroll event in XAML:

<ListBox PreviewMouseWheel="ScrollViewer_PreviewMouseWheel">

and re-publishing it in Code behind:

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

The solution is exactly for ListBox, it helped me with ListView.

I found this solution here:

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

Henton answered 30/12, 2017 at 16:26 Comment(3)
Thanks! Been looking for a long time now. It works!Indescribable
How would you solve this if use you were using touch to scroll instead of the mouse wheel?Primordial
I don't have ready solution and cannot test it but I'd start looking for similar touchscreen related events and republish them alike the solution I'd reposted.Henton
E
4

I like to create a behavior for this type of thing.

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

<ListView ItemsSource="{Binding Items}">
    <bhv:Interaction.Behaviors>
        <bhvs:NoScrollingBehavior/>
    </bhv:Interaction.Behaviors>
</ListView>

The behavior itself.

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);
    }
}
Expository answered 2/11, 2020 at 19:22 Comment(1)
Thanks, I was looking for a solution using MVVM !Televise
J
1

Sorry for waking up such a old post. Actually, you can disable the ScrollViewer by using ScrollViewer's attached property.

<ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled"
         ScrollViewer.VerticalScrollBarVisibility="Disabled" ...
</ListBox>
Jointless answered 16/8, 2016 at 3:44 Comment(1)
This still seems to steal the wheeling eventPsychometry
L
0

Here is a variant with DependencyProperty, if you don't like behaviors

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

This is how it is used

<Listbox b:IgnoreScrollingBehavior.IgnoreScrolling="True".../>
Lupine answered 18/7, 2021 at 8:49 Comment(0)
P
-1

You can use this ! No Wheel stolen.

<ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled"
     ScrollViewer.VerticalScrollBarVisibility="Disabled" ...
</ListBox>
Pilar answered 24/11, 2017 at 21:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.