Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Synchronized scrolling of two ScrollViewers whenever any one is scrolled in wpf

Tags:

I have gone through the thread:

binding two VerticalScrollBars one to another

it has almost helped to achieve the goal but still there is something missing. It is that moving the scrollbars left-right or up-down gives expected behavior of scrolling in both of my scrollviewers but when we try to scroll using/clicking arrow buttons at the ends of these scrollbars in scrollviewers only one scrollviewer is scrolled which is not the expected behavior.

So what else we need to add/edit to solve this?

like image 210
Vikram_ Avatar asked Mar 01 '13 06:03

Vikram_


2 Answers

One way to do this is using the ScrollChanged event to update the other ScrollViewer

<ScrollViewer Name="sv1" Height="100"                HorizontalScrollBarVisibility="Auto"               ScrollChanged="ScrollChanged">     <Grid Height="1000" Width="1000" Background="Green" /> </ScrollViewer>  <ScrollViewer Name="sv2" Height="100"                HorizontalScrollBarVisibility="Auto"               ScrollChanged="ScrollChanged">     <Grid Height="1000" Width="1000" Background="Blue" /> </ScrollViewer>  private void ScrollChanged(object sender, ScrollChangedEventArgs e)     {         if (sender == sv1)         {             sv2.ScrollToVerticalOffset(e.VerticalOffset);             sv2.ScrollToHorizontalOffset(e.HorizontalOffset);         }         else         {             sv1.ScrollToVerticalOffset(e.VerticalOffset);             sv1.ScrollToHorizontalOffset(e.HorizontalOffset);         }     } 
like image 78
Eirik Avatar answered Oct 05 '22 01:10

Eirik


The question is for WPF, but in case anyone developing UWP stumbles upon this, I had to take a slightly different approach.
In UWP, when you set the scroll offset of the other scroll viewer (using ScrollViewer.ChangeView), it also triggers the ViewChanged event on the other scroll viewer, basically creating a loop, causing it to be very stuttery, and not work properly.

I resolved this by using a little time-out on handling the event, if the object being scrolled is not equal to the last object that handled the event.

XAML:

<ScrollViewer x:Name="ScrollViewer1" ViewChanged="SynchronizedScrollerOnViewChanged"> ... </ScrollViewer> <ScrollViewer x:Name="ScrollViewer2" ViewChanged="SynchronizedScrollerOnViewChanged"> ... </ScrollViewer> 

Code behind:

public sealed partial class MainPage {     private const int ScrollLoopbackTimeout = 500;      private object _lastScrollingElement;     private int _lastScrollChange = Environment.TickCount;      public SongMixerUserControl()     {         InitializeComponent();     }      private void SynchronizedScrollerOnViewChanged(object sender, ScrollViewerViewChangedEventArgs e)     {         if (_lastScrollingElement != sender && Environment.TickCount - _lastScrollChange < ScrollLoopbackTimeout) return;          _lastScrollingElement = sender;         _lastScrollChange = Environment.TickCount;          ScrollViewer sourceScrollViewer;         ScrollViewer targetScrollViewer;         if (sender == ScrollViewer1)         {             sourceScrollViewer = ScrollViewer1;             targetScrollViewer = ScrollViewer2;         }         else         {             sourceScrollViewer = ScrollViewer2;             targetScrollViewer = ScrollViewer1;         }          targetScrollViewer.ChangeView(null, sourceScrollViewer.VerticalOffset, null);     } } 

Note that the timeout is 500ms. This may seem a little long, but as UWP apps have an animation (or, easing, really) in their scrolling (when using the scroll wheel on a mouse), it causes the event to trigger for a few times within a few hundred milliseconds. This timeout seems to work perfectly.

like image 23
René Sackers Avatar answered Oct 05 '22 01:10

René Sackers