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

Я пытаюсь синхронизировать горизонтальную позицию прокрутки двух элементов управления 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.


person Philipp Schmid    schedule 16.11.2008    source источник


Ответы (5)


Для этого есть отличный фрагмент кода:

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

person greenoldman    schedule 03.01.2011

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

person Philipp Schmid    schedule 18.11.2008
comment
Ага. Однако я делал то же самое в прошлом. Просто кажется, что нам не нужно таким образом взламывать визуальное дерево. Просто еще один способ, которым WPF груб по краям. - person PeterAllenWebb; 13.05.2009
comment
Будьте осторожны, когда пользователь меняет визуальные темы - элементы управления затем получают новые шаблоны (= новые визуальные деревья), и вы будете ссылаться на неправильную программу просмотра прокрутки. Вы должны реагировать в OnApplyTemplate и искать фактический ScrollViewer каждый раз, когда он вызывается. См. msdn.microsoft.com/en-us/library / - person Tomáš Kafka; 06.07.2010

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

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

Интересно услышать, зачем вам это нужно.

person Kent Boogaart    schedule 16.11.2008
comment
У меня есть источник WPF Toolkit DataGrid от Codeplex, поэтому я мог бы найти его и раскрыть (не мой предпочтительный метод). Я складываю 2 сетки, чтобы получить эффект замороженной панели (как Excel). - person Philipp Schmid; 16.11.2008

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

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

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

xmlns: scroll = "clr-namespace: ScrollSynchronizer"

затем просто добавил это в обе мои таблицы данных и качает вашему дяде:

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

Вы можете обмануть сетку данных, чтобы предоставить ее ScrollViewer как общедоступное свойство для каждой сетки, когда, например, во время инициализации пользовательского элемента управления вызывается обработчик innerGridControl_ScrollChanged (). Чтобы раскрыть его, вы можете сделать свою сетку в файле представления 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 внутри внутренней сетки (если оно есть, например, если вы определяете TextBox со скроллером по умолчанию в одном из шаблонов данных столбца) для e.Handled установлено значение true, чтобы предотвратить его обработку обработчиком внешней сетки (это произошло из-за поведения маршрутизации по умолчанию для событий маршрута). В качестве альтернативы вы можете добавить дополнительную отметку if в e.OriginalSource или e.Source, чтобы отфильтровать событие прокрутки, которое вы собираетесь обрабатывать.

person mithocondria    schedule 23.01.2014