Synchronized Scrolling of Two Scrollviewers Whenever Any One Is Scrolled in Wpf

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

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);
}
}

Synchronized scrolling of two ScrollViewers with different content sizes

You have to calculate the scrolling position in a fraction of the total:

After scrolloffset on sv1 changes try something like

var scFract = sv1.HorizontalOffset / sv1.ScrollableWith;
sv2.ScrollToHorizontalOffset(sv2.ScrollableWith * scFract);

How to synchronize two ScrollViewers?

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.

Synchronizing Two Rich Text Box Scroll bars in WPF

You can use the ScrollViewer.ScrollChanged routed event to listen for scrolling changes. Example:

<UniformGrid Rows="1" Width="300" Height="150" >
<RichTextBox x:Name="_rich1"
VerticalScrollBarVisibility="Auto"
ScrollViewer.ScrollChanged="RichTextBox_ScrollChanged" />
<RichTextBox x:Name="_rich2"
VerticalScrollBarVisibility="Auto"
ScrollViewer.ScrollChanged="RichTextBox_ScrollChanged" />
</UniformGrid>

Then, in the event handler you do the actual synchronizing (code stolen from inspired by this other answer):

private void RichTextBox_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
var textToSync = (sender == _rich1) ? _rich2 : _rich1;

textToSync.ScrollToVerticalOffset(e.VerticalOffset);
textToSync.ScrollToHorizontalOffset(e.HorizontalOffset);
}

How to link scrollbar and scrollviewer

Ok, solved this. Was actually quite straightforward.

Have since found Wpf binding to a function, which should help anyone else interested. Its VB but should be clear enough.

Cheers

Further to above: I subclassed ScrollBar and passed in the ScrollViewer I wanted to bind.
Seems to work ok.

public class ScrollViewerBoundScrollBar : ScrollBar
{
private ScrollViewer _scrollViewer;
public ScrollViewer BoundScrollViewer { get { return _scrollViewer; } set { _scrollViewer = value; UpdateBindings(); } }

public ScrollViewerBoundScrollBar( ScrollViewer scrollViewer, Orientation o ) : base()
{
this.Orientation = o;
BoundScrollViewer = _scrollViewer;
}

public ScrollViewerBoundScrollBar() : base()
{
}

private void UpdateBindings()
{
this.AddHandler(ScrollBar.ScrollEvent, new ScrollEventHandler(OnScroll));
_scrollViewer.AddHandler(ScrollViewer.ScrollChangedEvent, new ScrollChangedEventHandler(BoundScrollChanged));
this.Minimum = 0;
if (Orientation == Orientation.Horizontal)
{
this.SetBinding(ScrollBar.MaximumProperty, (new Binding("ScrollableWidth") { Source = _scrollViewer, Mode = BindingMode.OneWay }));
this.SetBinding(ScrollBar.ViewportSizeProperty, (new Binding("ViewportWidth") { Source = _scrollViewer, Mode = BindingMode.OneWay }));
}
else
{
this.SetBinding(ScrollBar.MaximumProperty, (new Binding("ScrollableHeight") { Source = _scrollViewer, Mode = BindingMode.OneWay }));
this.SetBinding(ScrollBar.ViewportSizeProperty, (new Binding("ViewportHeight") { Source = _scrollViewer, Mode = BindingMode.OneWay }));
}
this.LargeChange = 242;
this.SmallChange = 16;
}

public void BoundScrollChanged(object sender, ScrollChangedEventArgs e)
{
switch (this.Orientation)
{
case Orientation.Horizontal:
this.Value = e.HorizontalOffset;
break;
case Orientation.Vertical:
this.Value = e.VerticalOffset;
break;
default:
break;
}
}

public void OnScroll(object sender, ScrollEventArgs e)
{
switch(this.Orientation)
{
case Orientation.Horizontal:
this.BoundScrollViewer.ScrollToHorizontalOffset(e.NewValue);
break;
case Orientation.Vertical:
this.BoundScrollViewer.ScrollToVerticalOffset(e.NewValue);
break;
default:
break;
}
}
}


Related Topics



Leave a reply



Submit