I currently have two ScrollViewer's containing alternate views of the same collection. I have bound the scrolling of the two scrollviewers together by handling the ScrollChanged event and using ScrollToVerticalOffset.
For presentation reasons I have set both ScrollViewer scrollbars to hidden and want to control them both from a seperate ScrollBar.
This seems to not be straightforward. I recall seeing a blog about it a few months ago but I can't find it again.
Can anyone point me in the direction of some useful resources or give me a shove in the right direction to how it might be achieved.
Thanks in advance.
Great, just what I needed. I extended a bit so that the scrollviewer can be set from xaml as well using a dependency property. In xaml:
<local:BindableScrollBar BoundScrollViewer ="{Binding ElementName=ScrollViewer}" Orientation="Vertical" />
Code:
/// <summary>
/// An extended scrollbar that can be bound to an external scrollviewer.
/// </summary>
public class BindableScrollBar : ScrollBar
{
public ScrollViewer BoundScrollViewer
{
get { return (ScrollViewer)GetValue(BoundScrollViewerProperty); }
set { SetValue(BoundScrollViewerProperty, value); }
}
// Using a DependencyProperty as the backing store for BoundScrollViewer. This enables animation, styling, binding, etc...
public static readonly DependencyProperty BoundScrollViewerProperty =
DependencyProperty.Register("BoundScrollViewer", typeof(ScrollViewer), typeof(BindableScrollBar), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnBoundScrollViewerPropertyChanged)));
private static void OnBoundScrollViewerPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
BindableScrollBar sender = d as BindableScrollBar;
if (sender != null && e.NewValue != null)
{
sender.UpdateBindings();
}
}
/// <summary>
/// Initializes a new instance of the <see cref="BindableScrollBar"/> class.
/// </summary>
/// <param name="scrollViewer">The scroll viewer.</param>
/// <param name="o">The o.</param>
public BindableScrollBar(ScrollViewer scrollViewer, Orientation o)
: base()
{
this.Orientation = o;
BoundScrollViewer = scrollViewer;
}
/// <summary>
/// Initializes a new instance of the <see cref="BindableScrollBar"/> class.
/// </summary>
public BindableScrollBar() : base() { }
private void UpdateBindings()
{
AddHandler(ScrollBar.ScrollEvent, new ScrollEventHandler(OnScroll));
BoundScrollViewer.AddHandler(ScrollViewer.ScrollChangedEvent, new ScrollChangedEventHandler(BoundScrollChanged));
Minimum = 0;
if (Orientation == Orientation.Horizontal)
{
SetBinding(ScrollBar.MaximumProperty, (new Binding("ScrollableWidth") { Source = BoundScrollViewer, Mode = BindingMode.OneWay }));
SetBinding(ScrollBar.ViewportSizeProperty, (new Binding("ViewportWidth") { Source = BoundScrollViewer, Mode = BindingMode.OneWay }));
}
else
{
this.SetBinding(ScrollBar.MaximumProperty, (new Binding("ScrollableHeight") { Source = BoundScrollViewer, Mode = BindingMode.OneWay }));
this.SetBinding(ScrollBar.ViewportSizeProperty, (new Binding("ViewportHeight") { Source = BoundScrollViewer, Mode = BindingMode.OneWay }));
}
LargeChange = 242;
SmallChange = 16;
}
private 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;
}
}
private 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;
}
}
}
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;
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With