从ListView冒泡滚动事件到其父

在我的WPF应用程序中,我有一个ListViewScrollViewer.VerticalScrollBarVisibility被设置为Disabled 。 它包含在ScrollViewer 。 当我尝试在ListView上使用鼠标滚轮时,外部ScrollViewer不滚动,因为ListView正在捕获滚动事件。

我如何强制ListView允许滚动事件向上ScrollViewerScrollViewer

您需要捕获内部列表视图中的预览鼠标滚轮事件

 ctl.PreviewMouseWheel += PreviewMouseWheel; 

然后停止滚动列表视图的事件并在父列表视图中引发事件。

 private static void PreviewMouseWheel(object sender, MouseWheelEventArgs e) { if (!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); } } 

信贷去@罗伯特 – 瓦格纳谁解决了这个几个月前。

另一个很好的解决scheme使用附加的行 我喜欢它,因为它解决了Control的解决scheme。

创build一个不滚动的行为,它将捕获PreviewMouseWheel(Tunneling)事件并引发一个新的MouseWheelEvent(Bubbling)

 public sealed class IgnoreMouseWheelBehavior : Behavior<UIElement> { protected override void OnAttached( ) { base.OnAttached( ); AssociatedObject.PreviewMouseWheel += AssociatedObject_PreviewMouseWheel ; } protected override void OnDetaching( ) { AssociatedObject.PreviewMouseWheel -= AssociatedObject_PreviewMouseWheel; base.OnDetaching( ); } void AssociatedObject_PreviewMouseWheel(object sender, MouseWheelEventArgs e) { e.Handled = true; var e2 = new MouseWheelEventArgs(e.MouseDevice,e.Timestamp,e.Delta); e2.RoutedEvent = UIElement.MouseWheelEvent; AssociatedObject.RaiseEvent(e2); } } 

然后将行为附加到嵌套ScrollViewers案件的任何UIElement

  <ListBox Name="ForwardScrolling"> <i:Interaction.Behaviors> <local:IgnoreMouseWheelBehavior /> </i:Interaction.Behaviors> </ListBox> 

所有功劳乔什爱因斯坦博客

如果你来这里寻找一个解决scheme,只有当孩子在顶部,向上滚动或者底部向下滚动时,才会冒泡。这里有一个解决scheme。 我只用DataGridtesting过,但是它也可以和其他控件一起使用。

 public class ScrollParentWhenAtMax : Behavior<FrameworkElement> { protected override void OnAttached() { base.OnAttached(); this.AssociatedObject.PreviewMouseWheel += PreviewMouseWheel; } protected override void OnDetaching() { this.AssociatedObject.PreviewMouseWheel -= PreviewMouseWheel; base.OnDetaching(); } private void PreviewMouseWheel(object sender, MouseWheelEventArgs e) { var scrollViewer = GetVisualChild<ScrollViewer>(this.AssociatedObject); var scrollPos = scrollViewer.ContentVerticalOffset; if ((scrollPos == scrollViewer.ScrollableHeight && e.Delta < 0) || (scrollPos == 0 && e.Delta > 0)) { e.Handled = true; var e2 = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta); e2.RoutedEvent = UIElement.MouseWheelEvent; AssociatedObject.RaiseEvent(e2); } } private static T GetVisualChild<T>(DependencyObject parent) where T : Visual { T child = default(T); int numVisuals = VisualTreeHelper.GetChildrenCount(parent); for (int i = 0; i < numVisuals; i++) { Visual v = (Visual)VisualTreeHelper.GetChild(parent, i); child = v as T; if (child == null) { child = GetVisualChild<T>(v); } if (child != null) { break; } } return child; } } 

要附加此行为,请将以下XMLNS和XAML添加到您的元素中:

  xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" <i:Interaction.Behaviors> <shared:ScrollParentWhenAtMax /> </i:Interaction.Behaviors> 

有不同的方法取决于你的确切情况,但我发现这很好地工作。 假设你的基本情况是这样的:

 <Window Height="200" Width="200"> <Grid> <ScrollViewer Name="sViewer"> <StackPanel> <Label Content="Scroll works here" Margin="10" /> <ListView Name="listTest" Margin="10" PreviewMouseWheel="listTest_PreviewMouseWheel" ScrollViewer.VerticalScrollBarVisibility="Disabled"> <ListView.ItemsSource> <Int32Collection> 1,2,3,4,5,6,7,8,9,10 </Int32Collection> </ListView.ItemsSource> <ListView.View> <GridView> <GridViewColumn Header="Column 1" /> </GridView> </ListView.View> </ListView> </StackPanel> </ScrollViewer> </Grid> </Window> 

在PreviewMouseWheel期间引发MouseWheelEvent似乎强制ScrollViewer工作。 我希望我知道为什么,这似乎很违反直觉。

 private void listTest_PreviewMouseWheel(object sender, MouseWheelEventArgs e) { e.Handled = true; MouseWheelEventArgs e2 = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta); e2.RoutedEvent = UIElement.MouseWheelEvent; listTest.RaiseEvent(e2); } 

我的用例略有不同。 我有一个非常大的滚动查看器,并在底部另一个滚动查看器有600的maxheight。我想滚动整个页面的底部,直到我滚动内容滚动查看器。 这可以确保您在开始滚动之前先看到整个滚动查看器。

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Interactivity; using System.Windows.Media; namespace CleverScroller.Helper { public class ScrollParentWhenAtMax : Behavior<FrameworkElement> { protected override void OnAttached() { base.OnAttached(); this.AssociatedObject.PreviewMouseWheel += PreviewMouseWheel; } protected override void OnDetaching() { this.AssociatedObject.PreviewMouseWheel -= PreviewMouseWheel; base.OnDetaching(); } private void PreviewMouseWheel(object sender, MouseWheelEventArgs e) { if (e.Delta < 0) { var outerscroller = GetVisualParent<ScrollViewer>(this.AssociatedObject); if (outerscroller.ContentVerticalOffset < outerscroller.ScrollableHeight) { e.Handled = true; var e2 = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta); e2.RoutedEvent = UIElement.MouseWheelEvent; AssociatedObject.RaiseEvent(e2); } } else { var scrollViewer = GetVisualChild<ScrollViewer>(this.AssociatedObject); var scrollPos = scrollViewer.ContentVerticalOffset; if ((scrollPos == scrollViewer.ScrollableHeight && e.Delta < 0) || (scrollPos == 0 && e.Delta > 0)) { e.Handled = true; var e2 = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta); e2.RoutedEvent = UIElement.MouseWheelEvent; AssociatedObject.RaiseEvent(e2); } } } private static T GetVisualChild<T>(DependencyObject parent) where T : Visual { T child = default(T); int numVisuals = VisualTreeHelper.GetChildrenCount(parent); for (int i = 0; i < numVisuals; i++) { Visual v = (Visual)VisualTreeHelper.GetChild(parent, i); child = v as T; if (child == null) { child = GetVisualChild<T>(v); } if (child != null) { break; } } return child; } private static T GetVisualParent<T>(DependencyObject parent) where T : Visual { T obj = default(T); Visual v = (Visual)VisualTreeHelper.GetParent(parent); do { v = (Visual)VisualTreeHelper.GetParent(v); obj = v as T; } while (obj == null); return obj; } } } 

好吧,因为我一直在这里,但我不得不对此发表评论。 任何预览事件隧道,那么为什么我们冒泡呢? 停在父母的隧道,并完成它。 在父级添加一个PreviewMouseWheel事件。

  private void UIElement_OnPreviewMouseWheel(object sender, MouseWheelEventArgs e) { var scrollViewer = FindName("LeftPanelScrollViwer"); // name your parent mine is a scrollViewer ((ScrollViewer) scrollViewer)?.ScrollToVerticalOffset(e.Delta); e.Handled = true; } 

您也可以使用附加的行为来实现相同的function。 这具有不需要System.Windows.Interactivity库的优点。 这个逻辑已经从其他的答案中拿出来了,只是执行是不一样的。

 public static class IgnoreScrollBehaviour { public static readonly DependencyProperty IgnoreScrollProperty = DependencyProperty.RegisterAttached("IgnoreScroll", typeof(bool), typeof(IgnoreScrollBehaviour), new PropertyMetadata(OnIgnoreScollChanged)); public static void SetIgnoreScroll(DependencyObject o, string value) { o.SetValue(IgnoreScrollProperty, value); } public static string GetIgnoreScroll(DependencyObject o) { return (string)o.GetValue(IgnoreScrollProperty); } private static void OnIgnoreScollChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { bool ignoreScoll = (bool)e.NewValue; UIElement element = d as UIElement; if (element == null) return; if (ignoreScoll) { element.PreviewMouseWheel += Element_PreviewMouseWheel; } else { element.PreviewMouseWheel -= Element_PreviewMouseWheel; } } private static void Element_PreviewMouseWheel(object sender, MouseWheelEventArgs e) { UIElement element = sender as UIElement; if (element != null) { e.Handled = true; var e2 = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta); e2.RoutedEvent = UIElement.MouseWheelEvent; element.RaiseEvent(e2); } } } 

然后在XAML中:

 <DataGrid ItemsSource="{Binding Items}"> <DataGrid.RowDetailsTemplate> <DataTemplate> <ListView ItemsSource="{Binding Results}" behaviours:IgnoreScrollBehaviour.IgnoreScroll="True"> <ListView.ItemTemplate> <DataTemplate> ... </DataTemplate> </ListView.ItemTemplate> </ListView> </DataTemplate> </DataGrid.RowDetailsTemplate> <DataGrid.Columns> ... </DataGrid.Columns> </DataGrid>