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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
6
0
14 412
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

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

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

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

У меня есть источник WPF Toolkit DataGrid от Codeplex, поэтому я мог бы найти его и раскрыть (не мой предпочтительный метод). Я складываю 2 сетки, чтобы получить эффект замороженной панели (как Excel).

Philipp Schmid 16.11.2008 19:16
Ответ принят как подходящий

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

Да уж. Однако я делал то же самое в прошлом. Просто кажется, что нам не нужно таким образом взламывать визуальное дерево. Просто еще один способ, которым WPF груб по краям.

PeterAllenWebb 13.05.2009 18:32

Будьте осторожны, когда пользователь меняет визуальные темы - элементы управления затем получают новые шаблоны (= новые визуальные деревья), и вы будете ссылаться на неправильное средство просмотра прокрутки. Вы должны реагировать в OnApplyTemplate и искать фактический ScrollViewer каждый раз, когда он вызывается. См. msdn.microsoft.com/en-us/library/…

Tomáš Kafka 06.07.2010 20:40

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

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

Это отличное решение. У меня отлично работал в 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>

Вы можете обмануть сетку данных, чтобы предоставить ее 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, чтобы отфильтровать событие прокрутки, которое вы собираетесь обрабатывать.

Другие вопросы по теме