marce
marce

Reputation: 781

How to synchronize two ScrollViewers?

I have four areas. Three of them scroll. I have to synchronize their movement, so if the users moves areaA (horizontal only) areaC mimics the movement. If the user moves areaC areaA mimics the horizontal and areaB the vertical movement.

Since some areas can move ‘under’ others I don’t see a way to use a single ScrollView:

The 4 areas

Please note that I’m working on a Windows Phone 8.1 app targeting the WinRT runtime (if that’s the current name for it? No Silverlight it is.).

Regarding dimensions:

What I managed so far is to get them to synchronize by subscribing to the ViewChanging events, reading the NextView offset values from the event arg and calling ScrollTo[Vertical,Horizontal]Offset on the appropriate other scroller. As I said it works somehow, but they stutter quite a bit. Plus I haven't found a way to mimic the 'end of scrollview reached so content now gets compressed a bit up/downwards' effect.

//adding event handler
areaCScroller.ViewChanging += HandleAreaCScrolls;

//handler
void HandleAreaCScrolls(object sender, ScrollViewerViewChangingEventArgs e)
{
    areaAScroller.ScrollToHorizontalOffset(e.NewView.HorizontalOffset);
    areaBScroller.ScrollToVerticalOffset(e.NewView.VerticalOffset);
}

I also tried the FinalView values (resulting in StackOverFlowExceptions) and disabling inertia on the scrollers (which didn’t help but made them feel less ‘cool’).

So my question is: how can I do that in a better way?

I’m not exactly experienced with WPF/XAML so I might very well overlook (or have low Google foo) a control or feature which does what I need. Open for all suggestions (the layout itself is pretty much locked though). I looked here, here and here for example but they all do about the same I tried.

Upvotes: 3

Views: 1441

Answers (1)

PolarBear
PolarBear

Reputation: 203

I used ViewChanged from ScrollViewer. Everything worked fine for me. Here is the code from 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);
        }
    }

And XAML code from 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>

Try this, hope it helps.

Upvotes: 4

Related Questions