I currently have a ListBox whose ItemsSource collection is bound to a property on my viewmodel, of type IEnumerable. When that preoprty\'s reference changes, the ListBox update
Improved Fredrik Hedblad's answer to work with ObservableCollection:
public static class ItemsControlAttachedProperties
{
#region ScrollToTopOnItemsSourceChange Property
public static readonly DependencyProperty ScrollToTopOnItemsSourceChangeProperty =
DependencyProperty.RegisterAttached(
"ScrollToTopOnItemsSourceChange",
typeof(bool),
typeof(ItemsControlAttachedProperties),
new UIPropertyMetadata(false, OnScrollToTopOnItemsSourceChangePropertyChanged));
public static bool GetScrollToTopOnItemsSourceChange(DependencyObject obj)
{
return (bool) obj.GetValue(ScrollToTopOnItemsSourceChangeProperty);
}
public static void SetScrollToTopOnItemsSourceChange(DependencyObject obj, bool value)
{
obj.SetValue(ScrollToTopOnItemsSourceChangeProperty, value);
}
static void OnScrollToTopOnItemsSourceChangePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var itemsControl = obj as ItemsControl;
if (itemsControl == null)
{
throw new Exception("ScrollToTopOnItemsSourceChange Property must be attached to an ItemsControl based control.");
}
DependencyPropertyDescriptor descriptor =
DependencyPropertyDescriptor.FromProperty(ItemsControl.ItemsSourceProperty, typeof(ItemsControl));
if (descriptor != null)
{
if ((bool) e.NewValue)
{
descriptor.AddValueChanged(itemsControl, ItemsSourceChanged);
}
else
{
descriptor.RemoveValueChanged(itemsControl, ItemsSourceChanged);
}
}
}
static void ItemsSourceChanged(object sender, EventArgs e)
{
var itemsControl = sender as ItemsControl;
DoScrollToTop(itemsControl);
var collection = itemsControl.ItemsSource as INotifyCollectionChanged;
if (collection != null)
{
collection.CollectionChanged += (o, args) => DoScrollToTop(itemsControl);
}
}
static void DoScrollToTop(ItemsControl itemsControl)
{
EventHandler eventHandler = null;
eventHandler =
delegate
{
if (itemsControl.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
var scrollViewer = GetVisualChild(itemsControl);
scrollViewer.ScrollToTop();
itemsControl.ItemContainerGenerator.StatusChanged -= eventHandler;
}
};
itemsControl.ItemContainerGenerator.StatusChanged += eventHandler;
}
static T GetVisualChild(DependencyObject parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (var i = 0; i < numVisuals; i++)
{
var v = (Visual) VisualTreeHelper.GetChild(parent, i);
child = v as T ?? GetVisualChild(v);
if (child != null)
{
break;
}
}
return child;
}
#endregion
}