diff --git a/Infrastructure/ListBoxAutoScroll.cs b/Infrastructure/ListBoxAutoScroll.cs new file mode 100644 index 0000000..3866c34 --- /dev/null +++ b/Infrastructure/ListBoxAutoScroll.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Windows; +using System.Windows.Data; +using System.Windows.Controls; +using System.Windows.Media; + +namespace PettingZoo.Infrastructure +{ + // Source: https://social.msdn.microsoft.com/Forums/vstudio/en-US/0f524459-b14e-4f9a-8264-267953418a2d/trivial-listboxlistview-autoscroll?forum=wpf + // + // Slightly modified version; takes into account the current scroll position of the ListBox, + // so the end user can scroll up without getting dragged down again. + public static class ListBox + { + public static readonly DependencyProperty AutoScrollProperty = + DependencyProperty.RegisterAttached("AutoScroll", typeof (bool), typeof (System.Windows.Controls.ListBox), + new PropertyMetadata(false)); + + public static readonly DependencyProperty AutoScrollHandlerProperty = + DependencyProperty.RegisterAttached("AutoScrollHandler", typeof (AutoScrollHandler), + typeof (System.Windows.Controls.ListBox)); + + public static bool GetAutoScroll(System.Windows.Controls.ListBox instance) + { + return (bool) instance.GetValue(AutoScrollProperty); + } + + public static void SetAutoScroll(System.Windows.Controls.ListBox instance, bool value) + { + var oldHandler = (AutoScrollHandler) instance.GetValue(AutoScrollHandlerProperty); + if (oldHandler != null) + { + oldHandler.Dispose(); + instance.SetValue(AutoScrollHandlerProperty, null); + } + + instance.SetValue(AutoScrollProperty, value); + if (value) + instance.SetValue(AutoScrollHandlerProperty, new AutoScrollHandler(instance)); + } + } + + + public class AutoScrollHandler : DependencyObject, IDisposable + { + public static readonly DependencyProperty ItemsSourceProperty = + DependencyProperty.Register("ItemsSource", typeof (IEnumerable), + typeof (AutoScrollHandler), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, + ItemsSourcePropertyChanged)); + + private readonly System.Windows.Controls.ListBox target; + private ScrollViewer scrollViewer = null; + + public AutoScrollHandler(System.Windows.Controls.ListBox target) + { + this.target = target; + + var binding = new Binding("ItemsSource") + { + Source = this.target + }; + + BindingOperations.SetBinding(this, ItemsSourceProperty, binding); + } + + + public void Dispose() + { + BindingOperations.ClearBinding(this, ItemsSourceProperty); + } + + + public IEnumerable ItemsSource + { + get { return (IEnumerable) GetValue(ItemsSourceProperty); } + set { SetValue(ItemsSourceProperty, value); } + } + + + private static void ItemsSourcePropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + ((AutoScrollHandler) o).ItemsSourceChanged((IEnumerable) e.OldValue, (IEnumerable) e.NewValue); + } + + + private void ItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue) + { + var collection = oldValue as INotifyCollectionChanged; + if (collection != null) + collection.CollectionChanged -= Collection_CollectionChanged; + + collection = newValue as INotifyCollectionChanged; + if (collection != null) + collection.CollectionChanged += Collection_CollectionChanged; + } + + private void Collection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + if (e.Action != NotifyCollectionChangedAction.Add || e.NewItems == null || e.NewItems.Count < 1) + return; + + // Resolve on demand; in the constructor FindScrollViewer will return null + if (scrollViewer == null) + { + scrollViewer = FindScrollViewer(target); + if (scrollViewer == null) + return; + } + + if (Math.Abs(scrollViewer.VerticalOffset - scrollViewer.ScrollableHeight) < 1) + target.ScrollIntoView(e.NewItems[e.NewItems.Count - 1]); + } + + + private static ScrollViewer FindScrollViewer(DependencyObject parent) + { + var childCount = VisualTreeHelper.GetChildrenCount(parent); + for (var childIndex = 0; childIndex < childCount; childIndex++) + { + var child = VisualTreeHelper.GetChild(parent, childIndex); + var scrollViewer = (child as ScrollViewer); + if (scrollViewer != null) + return scrollViewer; + + scrollViewer = FindScrollViewer(child); + if (scrollViewer != null) + return scrollViewer; + } + + return null; + } + } +}