Как синхронизировать два ScrollViewers?
У меня есть четыре области. Три из них прокручиваются. Я должен синхронизировать их движение, поэтому, если пользователи перемещают область А (только горизонтальная), область С имитирует движение. Если пользователь перемещает areaC, areaA имитирует горизонтальное, а areaB - вертикальное движение.
Так как некоторые области могут перемещаться "под" другими, я не вижу способа использовать один ScrollView:
- С движется вертикально "под" А
- C движется горизонтально "под" B
- B перемещается вертикально "под" фиксированной областью
- А движется горизонтально "под" фиксированной областью
Обратите внимание, что я работаю над приложением Windows Phone 8.1, ориентированным на среду выполнения WinRT (если это текущее название для него? Нет Silverlight, это так).
Что касается размеров:
- AreaA и AreaC имеют одинаковую ширину
- areaB и areaC имеют одинаковую высоту
До сих пор мне удавалось синхронизировать их, подписавшись на ViewChanging
события, читая NextView
значения смещения от события arg и вызова ScrollTo[Vertical,Horizontal]Offset
на соответствующем другом скроллере. Как я уже сказал, это работает как-то, но они немного заикаются. Кроме того, я не нашел способа имитировать "достигнут конец прокрутки, поэтому контент теперь сжимается немного вверх / вниз".
//adding event handler
areaCScroller.ViewChanging += HandleAreaCScrolls;
//handler
void HandleAreaCScrolls(object sender, ScrollViewerViewChangingEventArgs e)
{
areaAScroller.ScrollToHorizontalOffset(e.NewView.HorizontalOffset);
areaBScroller.ScrollToVerticalOffset(e.NewView.VerticalOffset);
}
Я также попробовал значения FinalView (что привело к StackOverFlowExceptions) и отключил инерцию на скроллерах (что не помогло, но заставило их чувствовать себя менее "круто").
Итак, мой вопрос: как я могу сделать это лучше?
Я не совсем разбираюсь в WPF/XAML, поэтому я вполне могу пропустить (или иметь низкий Google foo) элемент управления или функцию, которая делает то, что мне нужно. Открыто для всех предложений (хотя сам макет в значительной степени заблокирован). Я посмотрел здесь, здесь и здесь, например, но они все делают примерно то же, что я пытался.
1 ответ
Я использовал ViewChanged из ScrollViewer. У меня все работало нормально. Вот код из MainPage.xaml.cs:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
var list = new List<int>();
for (int i = 0; i < 100; ++i)
{
list.Add(i);
}
ItemsControl1.ItemsSource = list;
ItemsControl2.ItemsSource = list;
}
private void ScrollViewer1_OnViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
if (ScrollViewer1.VerticalOffset != ScrollViewer2.VerticalOffset)
{
ScrollViewer2.ScrollToVerticalOffset(ScrollViewer1.VerticalOffset);
}
}
private void ScrollViewer2_OnViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
if (ScrollViewer1.VerticalOffset != ScrollViewer2.VerticalOffset)
{
ScrollViewer1.ScrollToVerticalOffset(ScrollViewer2.VerticalOffset);
}
}
И XAML-код из MainPage.xaml
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ScrollViewer Grid.Column="0"
x:Name="ScrollViewer1"
ViewChanged="ScrollViewer1_OnViewChanged">
<ItemsControl x:Name="ItemsControl1">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
<ScrollViewer Grid.Column="2"
x:Name="ScrollViewer2"
ViewChanged="ScrollViewer2_OnViewChanged">
<ItemsControl x:Name="ItemsControl2">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
Попробуйте это, надеюсь, это поможет.