Listview inside of scrollviewer prevents scrollviewer scroll
Solution 1
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).
Solution 2
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
});
}
}
}
Solution 3
Did you try disabling the ListView's
ScrollBars
?
<ListView ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled" />
Solution 4
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,
};
}
ConditionRacer
Updated on April 08, 2020Comments
-
ConditionRacer about 4 years
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. The listview scrolls its internal scrollviewer to the bottom and then continues to capture the mouse, preventing the containing scrollviewer from scrolling.
Any ideas on how to handle this?
-
ConditionRacer over 12 yearsI can't do that because the listviews may have more items than are visible.
-
Rachel over 12 years@Justin984 Aren't they in another
ScrollViewer
though? -
ConditionRacer over 12 yearsBah, I was going to draw a little ascii pic, but it's too much of a pain. The listboxes each have multiple entries. For example, list box 1 may have 10 entries, which require scrolling the listbox in order to view. But there are multiple listboxes which, together, are too large for the screen, so i have an outer scrollviewer to scroll the listboxes into view. Does that make sense?
-
Rachel over 12 years@Justin984 Perhaps you can do something where if the ListView is already scrolled to the bottom, it stops responding to scroll down events? Or instead passes them to the outer
ScrollViewer
? -
Peter almost 4 yearsThis solution worked nicely for me and it is easy to implement. I tried other suggested solutions, but only this worked for me (ListViewer inside a ScrollViewer)
-
Al Banna Techno logy about 3 years
-
iam.Carrot almost 3 yearsI was looking for exactly this. Been a long time windows 10 dev and this issue caught me by surprise in WPF. Thanks +1 :)
-
Kos almost 3 yearsHaha thanks! I haven't touched any C# since 2013, happy to see that this answer is still relevant!