Синхронизация позиций прокрутки для 2 WPF DataGrids

Я пытаюсь синхронизация горизонтальный свиток позиция от 2 WPF DataGrid управление.

Я подписываюсь на ScrollChanged событие первого DataGrid:

<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 для достижения синхронизации позиций прокрутки?

кстати, я использование замороженных столбцов, поэтому я не могу обернуть оба элемента управления DataGrid с помощью ScrollViewers.

5 ответов


в соответствии с группой продуктов Microsoft, пересечение визуального дерева для поиска ScrollViewer является рекомендуемым методом, как объяснено в их ответе на Codeplex.


есть большой кусок кода, чтобы сделать это:

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


У нас была такая же проблема при использовании сетки Infragistics, потому что это не (все еще не) поддерживает замороженные столбцы. Таким образом, у нас было две сетки бок о бок, которые были сделаны, чтобы выглядеть как один. Сетка слева не прокручивалась горизонтально, но сетка справа прокручивалась. Замерзшие колонны бедняги.

в любом случае, мы закончили тем, что просто дотянулись до визуального дерева и вытащили ScrollViewer сами. В конце концов, мы знали, что он был там - он просто не был выставлен объектом модель. Можно использовать аналогичный подход, если сетка WPF не предоставляет ScrollViewer. Или вы можете подкласс сетки и добавить функциональность, необходимую для этой работы.

интересно услышать, почему вам нужно это сделать.


вы можете обмануть datagrid, чтобы выставить его ScrollViewer как общедоступное свойство для каждой сетки, когда, например, обработчик innerGridControl_ScrollChanged() вызывается во время инициализации usercontrol. Чтобы предоставить его, вы можете создать свою сетку в файле представления xaml, а затем составить два из них в другом представлении xaml. Ниже код находится на 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.SetHorizontalOffset (e.HorizontalOffset) метод при возникновении другого события прокрутки. Ниже код находится в 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 внутри внутренней сетки (если таковое имеется, например, если вы определяете текстовое поле со скроллером по умолчанию в одном из шаблонов данных столбцов) имеет e.Обработано значение true, чтобы предотвратить обработку обработчика внешней сетки (это произошло из-за пузырьков по умолчанию поведение routedevents). В качестве альтернативы вы можете поставить дополнительные, если проверить на e.OriginalSource или e.Источник для фильтрации события прокрутки, которое вы собираетесь обработать.


Это отличное решение. Отлично работал для меня в WPF.

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

Я только что сделал ссылку на dll ScrollSynchronizer, добавил импорт xml:

xmlns: scroll= "clr-namespace:ScrollSynchronizer"

потом просто добавил Это в мой datagrids и Боб твой дядя:

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