using System.Collections.Generic; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Media; namespace YALV.Common { /// /// Refs: http://www.codeproject.com/Articles/39244/Scroll-Synchronization /// public class ScrollSynchronizer : DependencyObject { /// /// Identifies the attached property ScrollGroup /// public static readonly DependencyProperty ScrollGroupProperty = DependencyProperty.RegisterAttached("ScrollGroup", typeof(string), typeof(ScrollSynchronizer), new PropertyMetadata(new PropertyChangedCallback(OnScrollGroupChanged))); /// /// List of all registered scroll viewers. /// private static Dictionary scrollViewers = new Dictionary(); #if SILVERLIGHT /// /// List of all registered scrollbars. /// private static Dictionary horizontalScrollBars = new Dictionary(); /// /// List of all registered scrollbars. /// private static Dictionary verticalScrollBars = new Dictionary(); #endif /// /// Contains the latest horizontal scroll offset for each scroll group. /// private static Dictionary horizontalScrollOffsets = new Dictionary(); /// /// Contains the latest vertical scroll offset for each scroll group. /// private static Dictionary verticalScrollOffsets = new Dictionary(); /// /// Sets the value of the attached property ScrollGroup. /// /// Object on which the property should be applied. /// Value of the property. public static void SetScrollGroup(DependencyObject obj, string scrollGroup) { obj.SetValue(ScrollGroupProperty, scrollGroup); } /// /// Gets the value of the attached property ScrollGroup. /// /// Object for which the property should be read. /// Value of the property StartTime public static string GetScrollGroup(DependencyObject obj) { return (string)obj.GetValue(ScrollGroupProperty); } /// /// Occurs, when the ScrollGroupProperty has changed. /// /// The DependencyObject on which the property has changed value. /// Event data that is issued by any event that tracks changes to the effective value of this property. private static void OnScrollGroupChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var scrollViewer = d as ScrollViewer; if (scrollViewer != null) { if (!string.IsNullOrEmpty((string)e.OldValue)) { // Remove scrollviewer if (scrollViewers.ContainsKey(scrollViewer)) { #if SILVERLIGHT horizontalScrollBars.Remove(horizontalScrollBars.First(s => s.Value == scrollViewer).Key); verticalScrollBars.Remove(verticalScrollBars.First(s => s.Value == scrollViewer).Key); scrollViewer.Loaded += new RoutedEventHandler(ScrollViewer_Loaded); #else scrollViewer.ScrollChanged -= new ScrollChangedEventHandler(ScrollViewer_ScrollChanged); #endif scrollViewers.Remove(scrollViewer); } } if (!string.IsNullOrEmpty((string)e.NewValue)) { // If group already exists, set scrollposition of new scrollviewer to the scrollposition of the group if (horizontalScrollOffsets.Keys.Contains((string)e.NewValue)) { scrollViewer.ScrollToHorizontalOffset(horizontalScrollOffsets[(string)e.NewValue]); } else { horizontalScrollOffsets.Add((string)e.NewValue, scrollViewer.HorizontalOffset); } if (verticalScrollOffsets.Keys.Contains((string)e.NewValue)) { scrollViewer.ScrollToVerticalOffset(verticalScrollOffsets[(string)e.NewValue]); } else { verticalScrollOffsets.Add((string)e.NewValue, scrollViewer.VerticalOffset); } // Add scrollviewer scrollViewers.Add(scrollViewer, (string)e.NewValue); #if !SILVERLIGHT scrollViewer.ScrollChanged += new ScrollChangedEventHandler(ScrollViewer_ScrollChanged); #else scrollViewer.Loaded += new RoutedEventHandler(ScrollViewer_Loaded); #endif } } } #if !SILVERLIGHT /// /// Occurs, when the scroll offset of one scrollviewer has changed. /// /// The sender of the event. /// EventArgs of the event. private static void ScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e) { var changedScrollViewer = sender as ScrollViewer; if (e.VerticalChange != 0 && e.HorizontalChange != 0) Scroll(changedScrollViewer, true, true); else if (e.VerticalChange != 0) Scroll(changedScrollViewer, true, false); else if (e.HorizontalChange != 0) Scroll(changedScrollViewer, false, true); } #endif #if SILVERLIGHT /// /// Occurs, when the scroll viewer is loaded. /// /// The sender of the event. /// EventArgs of the event. private static void ScrollViewer_Loaded(object sender, RoutedEventArgs e) { var scrollViewer = (ScrollViewer)sender; var group = scrollViewers[scrollViewer]; scrollViewer.Opacity = 1; if (verticalScrollOffsets.Keys.Contains(group)) { scrollViewer.ScrollToVerticalOffset(verticalScrollOffsets[group]); } scrollViewer.ApplyTemplate(); var scrollViewerRoot = (FrameworkElement)VisualTreeHelper.GetChild(scrollViewer, 0); var horizontalScrollBar = (ScrollBar)scrollViewerRoot.FindName("HorizontalScrollBar"); var verticalScrollBar = (ScrollBar)scrollViewerRoot.FindName("VerticalScrollBar"); if (!horizontalScrollBars.Keys.Contains(horizontalScrollBar)) { horizontalScrollBars.Add(horizontalScrollBar, scrollViewer); } if (!verticalScrollBars.Keys.Contains(verticalScrollBar)) { verticalScrollBars.Add(verticalScrollBar, scrollViewer); } if (horizontalScrollBar != null) { horizontalScrollBar.Scroll += new ScrollEventHandler(HorizontalScrollBar_Scroll); horizontalScrollBar.ValueChanged += new RoutedPropertyChangedEventHandler(HorizontalScrollBar_ValueChanged); } if (verticalScrollBar != null) { verticalScrollBar.Scroll += new ScrollEventHandler(VerticalScrollBar_Scroll); verticalScrollBar.ValueChanged += new RoutedPropertyChangedEventHandler(VerticalScrollBar_ValueChanged); } } /// /// Occurs, when the horizontal scroll bar was moved. /// /// The sender of the event. /// EventArgs of the event. private static void HorizontalScrollBar_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) { var changedScrollBar = sender as ScrollBar; var changedScrollViewer = horizontalScrollBars[changedScrollBar]; Scroll(changedScrollViewer, false, true); } /// /// Occurs, when the vertical scroll bar was moved. /// /// The sender of the event. /// EventArgs of the event. private static void VerticalScrollBar_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) { var changedScrollBar = sender as ScrollBar; var changedScrollViewer = verticalScrollBars[changedScrollBar]; Scroll(changedScrollViewer, true, false); } /// /// Occurs, when the horizontal scroll bar was moved. /// /// The sender of the event. /// EventArgs of the event. private static void HorizontalScrollBar_Scroll(object sender, ScrollEventArgs e) { var changedScrollBar = sender as ScrollBar; var changedScrollViewer = horizontalScrollBars[changedScrollBar]; Scroll(changedScrollViewer, false, true); } /// /// Occurs, when the vertical scroll bar was moved. /// /// The sender of the event. /// EventArgs of the event. private static void VerticalScrollBar_Scroll(object sender, ScrollEventArgs e) { var changedScrollBar = sender as ScrollBar; var changedScrollViewer = verticalScrollBars[changedScrollBar]; Scroll(changedScrollViewer, true, false); } #endif /// /// Scrolls all scroll viewers of a group to the position of the selected scroll viewer. /// /// Sroll viewer, that specifies the current position of the group. private static void Scroll(ScrollViewer changedScrollViewer, bool verticalChange, bool horizontalChange) { var group = scrollViewers[changedScrollViewer]; verticalScrollOffsets[group] = changedScrollViewer.VerticalOffset; horizontalScrollOffsets[group] = changedScrollViewer.HorizontalOffset; foreach (var scrollViewer in scrollViewers.Where((s) => s.Value == group && s.Key != changedScrollViewer)) { if (verticalChange && scrollViewer.Key.VerticalOffset != changedScrollViewer.VerticalOffset) { scrollViewer.Key.ScrollToVerticalOffset(changedScrollViewer.VerticalOffset); } if (horizontalChange && scrollViewer.Key.HorizontalOffset != changedScrollViewer.HorizontalOffset) { scrollViewer.Key.ScrollToHorizontalOffset(changedScrollViewer.HorizontalOffset); } } } } }