同步2个WPF DataGrids的滚动位置

时间:2008-11-16 00:10:07

标签: wpf datagrid scroll

我正在尝试同步 2个 WPF DataGrid 控件的水平滚动位置

我正在订阅第一个DataGrid的 ScrollChanged 事件:

<toolkit:DataGrid x:Name="SourceGrid" ScrollViewer.ScrollChanged="SourceGrid_ScrollChanged">

我有第二个DataGrid:

<toolkit:DataGrid x:Name="TargetGrid">

在事件处理程序中,我尝试使用 IScrollInfo.SetHorizontalOffset ,但唉,DataGrid不会公开IScrollInfo

private void SourceGrid_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
    ((IScrollInfo)TargetGrid).SetHorizontalOffset(e.HorizontalOffset);
    // cast to IScrollInfo fails
}

还有另一种方法可以实现这一目标吗?或者TargetGrid上是否有另一个元素公开必要的IScrollInfo以实现滚动位置的同步?

BTW,我使用冻结列,所以我不能用ScrollViewers包装两个DataGrid控件。

5 个答案:

答案 0 :(得分:3)

根据Microsoft产品组,浏览可见树以查找ScrollViewer是推荐的方法,explained in their answer on Codeplex

答案 1 :(得分:2)

有很多代码可以做到这一点:

http://www.codeproject.com/KB/WPF/ScrollSynchronization.aspx

答案 2 :(得分:1)

使用Infragistics网格时遇到同样的问题,因为没有(仍然没有)支持冻结列。所以我们有两个并排的网格,看起来像一个。左边的网格不是水平滚动,而是右边的网格。穷人的冷冻柱。

无论如何,我们最终只是伸手进入视觉树并拔出了ScrollViewer。毕竟,我们知道它就在那里 - 它只是没有被对象模型暴露。如果WPF网格没有公开ScrollViewer,您可以使用类似的方法。或者您可以对网格进行子类化并添加使其工作所需的功能。

有兴趣听听你为什么需要这样做。

答案 3 :(得分:1)

例如,在初始化usercontrol期间调用innerGridControl_ScrollChanged()处理程序时,您可以欺骗datagrid将其ScrollViewer公开为每个网格的公共属性。 要公开它,您可以在xaml View文件中创建网格,然后在另一个xaml View中组合它们中的两个。 下面的代码在innerGrid.xaml.cs上,例如:

    public ScrollViewer Scroller { get; set; } // exposed ScrollViewer from the grid
    private bool _isFirstTimeLoaded = true; 

    private void innerGridControl_ScrollChanged(object sender, ScrollChangedEventArgs e)
    {
        if (_isFirstTimeLoaded) // just to save the code from casting and assignment after 1st time loaded
        {
            var scroller = (e.OriginalSource) as ScrollViewer;
            Scroller = scroller;
            _isFirstTimeLoaded = false;
        }
    }
OuterGridView.xaml上的

放置一个附加的事件处理程序定义:

<Views:innerGridView Grid.Row="1" Margin="2,0,2,2" DataContext="{Binding someCollection}" 
                                      x:Name="grid1Control"
                                      ScrollViewer.ScrollChanged="Grid1Attached_ScrollChanged"
                                      ></Views:innerGridView>

<Views:innerGridView Grid.Row="3" Margin="2,0,2,2" DataContext="{Binding someCollection}" 
                                      x:Name="grid2Control"
                                      ScrollViewer.ScrollChanged="Grid2Attached_ScrollChanged"
                                      ></Views:innerGridView>

然后在发生另一个滚动事件时访问该公共ScrollViewer.SetHorizo​​ntalOffset(e.Horizo​​ntalOffset)方法。 下面的代码是在OuterGridView.xaml.cs中的一个处理程序定义(

private void Grid1Attached_ScrollChanged(object sender, ScrollChangedEventArgs e)
    {
        if (e != null && !e.Handled)
        {
            if (e.HorizontalChange != 0.0)
            {
                grid2Control.Scroller.ScrollToHorizontalOffset(e.HorizontalOffset);
            }
            e.Handled = true;
        }
    }
private void Grid2Attached_ScrollChanged(object sender, ScrollChangedEventArgs e)
    {
        if (e != null && !e.Handled)
        {
            if (e.HorizontalChange != 0.0)
            {
                grid1Control.Scroller.ScrollToHorizontalOffset(e.HorizontalOffset);
            }
            e.Handled = true;
        }
    }

还要确保内部网格中的任何其他scroll_changed事件(如果有的话,例如,如果在其中一个列数据模板中定义带有默认滚动条的TextBox)将其e.Handled设置为true以防止外部网格的处理程序处理它(这是由于routedevents的默认冒泡行为)。或者,如果检查e.OriginalSource或e.Source以过滤您要处理的滚动事件,则可以添加其他内容。

答案 4 :(得分:0)

这是一个很好的解决方案。在WPF中为我工作得很好。

http://www.codeproject.com/Articles/39244/Scroll-Synchronization

我刚刚引用了ScrollSynchronizer dll,添加了一个xml导入:

xmlns:scroll =“clr-namespace:ScrollSynchronizer”

然后把这个添加到我的数据网格中并且让你的叔叔陷入困境:

<DataGrid.Resources>
   <Style TargetType="ScrollViewer">
     <Setter Property="scroll:ScrollSynchronizer.ScrollGroup" Value="Group1" />
   </Style>
</DataGrid.Resources>