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





У нас была такая же проблема при использовании сетки Infragistics, потому что она не сделал (все еще не поддерживает) замороженные столбцы. Таким образом, у нас были две сетки, расположенные рядом, которые выглядели как одна. Сетка слева не прокручивалась по горизонтали, а вот сетка справа прокручивалась. Замерзшие колонны бедняка.
Как бы то ни было, мы просто потянулись к визуальному дереву и сами вытащили ScrollViewer. В конце концов, мы знали, что это было - просто объектная модель не показывала его. Вы можете использовать аналогичный подход, если сетка WPF не предоставляет ScrollViewer. Или вы можете создать подкласс сетки и добавить функции, необходимые для работы.
Интересно услышать, зачем вам это нужно.
Согласно группе продуктов Microsoft, обход визуального дерева для поиска ScrollViewer является рекомендуемым методом, так как объяснил в своем ответе на Codeplex.
Да уж. Однако я делал то же самое в прошлом. Просто кажется, что нам не нужно таким образом взламывать визуальное дерево. Просто еще один способ, которым WPF груб по краям.
Будьте осторожны, когда пользователь меняет визуальные темы - элементы управления затем получают новые шаблоны (= новые визуальные деревья), и вы будете ссылаться на неправильное средство просмотра прокрутки. Вы должны реагировать в OnApplyTemplate и искать фактический ScrollViewer каждый раз, когда он вызывается. См. msdn.microsoft.com/en-us/library/…
Для этого есть отличный фрагмент кода:
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, чтобы отфильтровать событие прокрутки, которое вы собираетесь обрабатывать.
У меня есть источник WPF Toolkit DataGrid от Codeplex, поэтому я мог бы найти его и раскрыть (не мой предпочтительный метод). Я складываю 2 сетки, чтобы получить эффект замороженной панели (как Excel).