Wpf:如何实现在ScrollViewer内单独滚动DataGrid
实现ScrollViewer内DataGrid独立滚动的解决方案
当前实现情况
当前应用程序表现:
- 光标处于主ScrollViewer的蓝色区域或红色矩形区域时,滚动滚轮仅触发主ScrollViewer滚动。
预期效果
期望实现:光标悬停在DataGrid上时,滚动滚轮仅触发该DataGrid自身滚动,而非主ScrollViewer。但当前实际是在DataGrid上滚动时,主ScrollViewer仍会同步滚动。
现有代码
XAML代码
<ScrollViewer x:Name="ScrollViewer" PanningMode="Both" Background="Blue" HorizontalScrollBarVisibility="Auto" Padding="0,0,50,0" VerticalScrollBarVisibility="Auto" PreviewMouseWheel="DataGrid_PreviewMouseWheel"> <StackPanel> <DataGrid x:Name="DataGrid0" ItemsSource="{Binding Items}" Height="300" AutoGenerateColumns="True" PreviewMouseWheel="DataGrid_PreviewMouseWheel" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/> <Rectangle Height="200" Fill="Red"/> <DataGrid x:Name="DataGrid1" ItemsSource="{Binding Items}" Height="300" AutoGenerateColumns="True" PreviewMouseWheel="DataGrid_PreviewMouseWheel" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/> </StackPanel> </ScrollViewer>
C#代码
private void DataGrid_PreviewMouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e) { var args = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta); args.RoutedEvent = ScrollViewer.MouseWheelEvent; if (e.Source is DataGrid) { DataGrid0.RaiseEvent(args); } else { ScrollViewer.RaiseEvent(args); } }
解决方案
原代码的问题在于:固定触发DataGrid0的滚动事件,且未阻止事件冒泡到主ScrollViewer。正确逻辑是:优先让DataGrid自身处理滚动,当DataGrid滚到边界时,再让主ScrollViewer接管。
修改后的事件处理代码
private void DataGrid_PreviewMouseWheel(object sender, MouseWheelEventArgs e) { var dataGrid = sender as DataGrid; if (dataGrid == null) return; // 获取DataGrid内部的ScrollViewer var scrollViewer = FindVisualChild<ScrollViewer>(dataGrid); if (scrollViewer == null) return; // 判断DataGrid是否还能继续滚动:向上滚时未到顶部,或向下滚时未到底部 bool canScroll = (e.Delta > 0 && scrollViewer.VerticalOffset > 0) || (e.Delta < 0 && scrollViewer.VerticalOffset < scrollViewer.ScrollableHeight); if (canScroll) { // *阻止事件冒泡*,让DataGrid自行处理滚动 e.Handled = true; } // 若DataGrid已滚到边界,事件会自然传递给主ScrollViewer,实现滚动接力 } // 辅助方法:遍历Visual树查找子元素 private T FindVisualChild<T>(DependencyObject parent) where T : DependencyObject { for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++) { var child = VisualTreeHelper.GetChild(parent, i); if (child is T tChild) return tChild; var result = FindVisualChild<T>(child); if (result != null) return result; } return null; }
简化XAML
无需给主ScrollViewer绑定PreviewMouseWheel事件,移除该属性即可:
<ScrollViewer x:Name="ScrollViewer" PanningMode="Both" Background="Blue" HorizontalScrollBarVisibility="Auto" Padding="0,0,50,0" VerticalScrollBarVisibility="Auto"> <StackPanel> <DataGrid x:Name="DataGrid0" ItemsSource="{Binding Items}" Height="300" AutoGenerateColumns="True" PreviewMouseWheel="DataGrid_PreviewMouseWheel" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/> <Rectangle Height="200" Fill="Red"/> <DataGrid x:Name="DataGrid1" ItemsSource="{Binding Items}" Height="300" AutoGenerateColumns="True" PreviewMouseWheel="DataGrid_PreviewMouseWheel" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/> </StackPanel> </ScrollViewer>
修改后,鼠标在DataGrid上时优先滚动该DataGrid;当DataGrid滚到顶部/底部时,主ScrollViewer才会接管滚动,完全符合预期效果。
内容的提问来源于stack exchange,提问作者limeniye
相关产品推荐
相关产品推荐

