Created
July 30, 2018 14:15
-
-
Save punker76/7919c21b4b917972e39f8a58da6a96a6 to your computer and use it in GitHub Desktop.
ListBox scroll syncronisation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Windows; | |
using System.Windows.Controls; | |
using System.Windows.Media; | |
using System.Windows.Threading; | |
namespace ListBoxScrollSync | |
{ | |
public static class ScrollSync | |
{ | |
/// <summary> | |
/// List of all registered scroll viewers. | |
/// </summary> | |
private static readonly Dictionary<ScrollViewer, string> scrollViewers = new Dictionary<ScrollViewer, string>(); | |
/// <summary> | |
/// Contains the latest horizontal scroll offset for each scroll group. | |
/// </summary> | |
private static readonly Dictionary<string, double> horizontalScrollOffsets = new Dictionary<string, double>(); | |
/// <summary> | |
/// Contains the latest vertical scroll offset for each scroll group. | |
/// </summary> | |
private static readonly Dictionary<string, double> verticalScrollOffsets = new Dictionary<string, double>(); | |
/// <summary> | |
/// Identifies the attached property ScrollGroup | |
/// </summary> | |
public static readonly DependencyProperty ScrollGroupProperty | |
= DependencyProperty.RegisterAttached( | |
"ScrollGroup", | |
typeof(string), | |
typeof(ScrollSync), | |
new PropertyMetadata(OnScrollGroupChanged)); | |
/// <summary> | |
/// Sets the value of the attached property ScrollGroup. | |
/// </summary> | |
/// <param name="obj">Object on which the property should be applied.</param> | |
/// <param name="scrollGroup">Value of the property.</param> | |
public static void SetScrollGroup(DependencyObject obj, string scrollGroup) | |
{ | |
obj.SetValue(ScrollGroupProperty, scrollGroup); | |
} | |
/// <summary> | |
/// Gets the value of the attached property ScrollGroup. | |
/// </summary> | |
/// <param name="obj">Object for which the property should be read.</param> | |
/// <returns>Value of the property StartTime</returns> | |
public static string GetScrollGroup(DependencyObject obj) | |
{ | |
return (string) obj.GetValue(ScrollGroupProperty); | |
} | |
private static T GetDescendant<T>(DependencyObject element) where T : Visual | |
{ | |
if (element == null) | |
{ | |
return default(T); | |
} | |
if (element is T) | |
{ | |
return (T) element; | |
} | |
(element as FrameworkElement)?.ApplyTemplate(); | |
T foundElement = null; | |
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++) | |
{ | |
var nextChild = VisualTreeHelper.GetChild(element, i); | |
foundElement = GetDescendant<T>(nextChild); | |
if (foundElement != null) | |
{ | |
break; | |
} | |
} | |
return foundElement; | |
} | |
/// <summary> | |
/// Occurs, when the ScrollGroupProperty has changed. | |
/// </summary> | |
/// <param name="d">The DependencyObject on which the property has changed value.</param> | |
/// <param name="e">Event data that is issued by any event that tracks changes to the effective value of this property.</param> | |
private static void OnScrollGroupChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) | |
{ | |
var scrollGroupChangedAction = new Action<ScrollViewer>((sv) => | |
{ | |
if (sv != null) | |
{ | |
if (!string.IsNullOrEmpty((string) e.OldValue)) | |
{ | |
// Remove scrollviewer | |
if (scrollViewers.ContainsKey(sv)) | |
{ | |
sv.ScrollChanged -= ScrollViewer_ScrollChanged; | |
scrollViewers.Remove(sv); | |
} | |
} | |
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)) | |
{ | |
sv.ScrollToHorizontalOffset(horizontalScrollOffsets[(string) e.NewValue]); | |
} | |
else | |
{ | |
horizontalScrollOffsets.Add((string) e.NewValue, sv.HorizontalOffset); | |
} | |
if (verticalScrollOffsets.Keys.Contains((string) e.NewValue)) | |
{ | |
sv.ScrollToVerticalOffset(verticalScrollOffsets[(string) e.NewValue]); | |
} | |
else | |
{ | |
verticalScrollOffsets.Add((string) e.NewValue, sv.VerticalOffset); | |
} | |
// Add scrollviewer | |
scrollViewers.Add(sv, (string) e.NewValue); | |
sv.ScrollChanged += ScrollViewer_ScrollChanged; | |
} | |
} | |
}); | |
var listBox = d as ListBox; | |
var scrollViewer = GetDescendant<ScrollViewer>(listBox); | |
if (scrollViewer != null) | |
{ | |
scrollGroupChangedAction(scrollViewer); | |
} | |
else | |
{ | |
listBox?.Dispatcher?.BeginInvoke( | |
DispatcherPriority.Loaded, | |
new Action(() => { scrollGroupChangedAction(GetDescendant<ScrollViewer>(listBox)); })); | |
} | |
} | |
/// <summary> | |
/// Occurs, when the scroll offset of one scrollviewer has changed. | |
/// </summary> | |
/// <param name="sender">The sender of the event.</param> | |
/// <param name="e">EventArgs of the event.</param> | |
private static void ScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e) | |
{ | |
var changedScrollViewer = (ScrollViewer) sender; | |
if (e.VerticalChange != 0 && (changedScrollViewer.VerticalScrollBarVisibility == ScrollBarVisibility.Auto || changedScrollViewer.VerticalScrollBarVisibility == ScrollBarVisibility.Visible)) | |
{ | |
Scroll(changedScrollViewer); | |
} | |
else if (e.HorizontalChange != 0 && (changedScrollViewer.HorizontalScrollBarVisibility == ScrollBarVisibility.Auto || changedScrollViewer.HorizontalScrollBarVisibility == ScrollBarVisibility.Visible)) | |
{ | |
Scroll(changedScrollViewer); | |
} | |
} | |
/// <summary> | |
/// Scrolls all scroll viewers of a group to the position of the selected scroll viewer. | |
/// </summary> | |
/// <param name="changedScrollViewer">Sroll viewer, that specifies the current position of the group.</param> | |
private static void Scroll(ScrollViewer changedScrollViewer) | |
{ | |
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)) | |
{ | |
scrollViewer.Key.ScrollChanged -= ScrollViewer_ScrollChanged; | |
if (scrollViewer.Key.VerticalOffset != changedScrollViewer.VerticalOffset) | |
{ | |
scrollViewer.Key.ScrollToVerticalOffset(changedScrollViewer.VerticalOffset); | |
} | |
if (scrollViewer.Key.HorizontalOffset != changedScrollViewer.HorizontalOffset) | |
{ | |
scrollViewer.Key.ScrollToHorizontalOffset(changedScrollViewer.HorizontalOffset); | |
} | |
scrollViewer.Key.ScrollChanged += ScrollViewer_ScrollChanged; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment