I have an attached behavior to an ItemsControl that scrolls down to the bottom whenever a new item is added. Since I am working on a chat type program, I don't want it to scroll if the user has the scrollbar anywhere other than the very bottom as that would be very annoying otherwise(Some chat programs do this and it's awful).
How do I accomplish this? I don't know how to access the wrapping ScrollViewer, or otherwise figure out if I need to bring it into view or not.
This is the behavior class that I actually got from someone on StackOverflow. I'm still learning about behaviors myself.
public class ScrollOnNewItem : Behavior<ItemsControl>
{
protected override void OnAttached()
{
AssociatedObject.Loaded += OnLoaded;
AssociatedObject.Unloaded += OnUnLoaded;
}
protected override void OnDetaching()
{
AssociatedObject.Loaded -= OnLoaded;
AssociatedObject.Unloaded -= OnUnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
var incc = AssociatedObject.ItemsSource as INotifyCollectionChanged;
if (incc == null) return;
incc.CollectionChanged += OnCollectionChanged;
}
private void OnUnLoaded(object sender, RoutedEventArgs e)
{
var incc = AssociatedObject.ItemsSource as INotifyCollectionChanged;
if (incc == null) return;
incc.CollectionChanged -= OnCollectionChanged;
}
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
int count = AssociatedObject.Items.Count;
if (count == 0)
return;
var item = AssociatedObject.Items[count - 1];
var frameworkElement = AssociatedObject.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement;
if (frameworkElement == null) return;
frameworkElement.BringIntoView();
}
}
}
Okay, here's the answer I came up with for myself.
I figured out that there is a GetSelfAndAncestors method for dependency objects. Using that, I am able to get the ScrollViewer ancestor(if there is one) of my AssociatedObject(the ItemsControl) and manipulate it with that.
So I added this field to my behavior
And in the OnLoaded event handler I assigned it with the following code
And in the OnCollectionChanged event handler, I went ahead and wrapped all the logic in an if statement as follows
So all together, the code looks like the following
As asked in the comments, to use a behavior, I just need to add a new xmlns to my xaml file of the area in code that contains my behavior.
Then on the control I just add on the behavior.
The i class is just the interactivity namespace. xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
There is another way to implement this behavior. This way is easier than above. All you should do is invoke a method like below:
It is suitable for
TextBox
too.