You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 03:56:13