添加新项目时,如何使ListBox自动滚动? [英] How can I have a ListBox auto-scroll when a new item is added?
问题描述
我有一个WPF ListBox,它设置为水平滚动. ItemsSource绑定到我的ViewModel类中的ObservableCollection.每次添加新项目时,我都希望ListBox滚动到右侧,以便可以查看新项目.
I have a WPF ListBox that is set to scroll horizontally. The ItemsSource is bound to an ObservableCollection in my ViewModel class. Every time a new item is added, I want the ListBox to scroll to the right so that the new item is viewable.
ListBox是在DataTemplate中定义的,所以我无法通过文件后面代码中的名称访问ListBox.
The ListBox is defined in a DataTemplate, so I am unable to access the ListBox by name in my code behind file.
我如何才能使ListBox始终滚动以显示最新添加的项目?
How can I get a ListBox to always scroll to show a latest added item?
我想知道何时在ListBox上添加了一个新项目,但是我没有看到执行此操作的事件.
I would like a way to know when the ListBox has a new item added to it, but I do not see an event that does this.
推荐答案
您可以通过使用附加属性来扩展ListBox的行为.在您的情况下,我将定义一个名为ScrollOnNewItem
的附加属性,将其设置为true
时,它会钩住列表框项目源的INotifyCollectionChanged
事件,并在检测到新项目时将列表框滚动到该属性.
You can extend the behavior of the ListBox by using attached properties. In your case I would define an attached property called ScrollOnNewItem
that when set to true
hooks into the INotifyCollectionChanged
events of the list box items source and upon detecting a new item, scrolls the list box to it.
示例:
class ListBoxBehavior
{
static readonly Dictionary<ListBox, Capture> Associations =
new Dictionary<ListBox, Capture>();
public static bool GetScrollOnNewItem(DependencyObject obj)
{
return (bool)obj.GetValue(ScrollOnNewItemProperty);
}
public static void SetScrollOnNewItem(DependencyObject obj, bool value)
{
obj.SetValue(ScrollOnNewItemProperty, value);
}
public static readonly DependencyProperty ScrollOnNewItemProperty =
DependencyProperty.RegisterAttached(
"ScrollOnNewItem",
typeof(bool),
typeof(ListBoxBehavior),
new UIPropertyMetadata(false, OnScrollOnNewItemChanged));
public static void OnScrollOnNewItemChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var listBox = d as ListBox;
if (listBox == null) return;
bool oldValue = (bool)e.OldValue, newValue = (bool)e.NewValue;
if (newValue == oldValue) return;
if (newValue)
{
listBox.Loaded += ListBox_Loaded;
listBox.Unloaded += ListBox_Unloaded;
var itemsSourcePropertyDescriptor = TypeDescriptor.GetProperties(listBox)["ItemsSource"];
itemsSourcePropertyDescriptor.AddValueChanged(listBox, ListBox_ItemsSourceChanged);
}
else
{
listBox.Loaded -= ListBox_Loaded;
listBox.Unloaded -= ListBox_Unloaded;
if (Associations.ContainsKey(listBox))
Associations[listBox].Dispose();
var itemsSourcePropertyDescriptor = TypeDescriptor.GetProperties(listBox)["ItemsSource"];
itemsSourcePropertyDescriptor.RemoveValueChanged(listBox, ListBox_ItemsSourceChanged);
}
}
private static void ListBox_ItemsSourceChanged(object sender, EventArgs e)
{
var listBox = (ListBox)sender;
if (Associations.ContainsKey(listBox))
Associations[listBox].Dispose();
Associations[listBox] = new Capture(listBox);
}
static void ListBox_Unloaded(object sender, RoutedEventArgs e)
{
var listBox = (ListBox)sender;
if (Associations.ContainsKey(listBox))
Associations[listBox].Dispose();
listBox.Unloaded -= ListBox_Unloaded;
}
static void ListBox_Loaded(object sender, RoutedEventArgs e)
{
var listBox = (ListBox)sender;
var incc = listBox.Items as INotifyCollectionChanged;
if (incc == null) return;
listBox.Loaded -= ListBox_Loaded;
Associations[listBox] = new Capture(listBox);
}
class Capture : IDisposable
{
private readonly ListBox listBox;
private readonly INotifyCollectionChanged incc;
public Capture(ListBox listBox)
{
this.listBox = listBox;
incc = listBox.ItemsSource as INotifyCollectionChanged;
if (incc != null)
{
incc.CollectionChanged += incc_CollectionChanged;
}
}
void incc_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
listBox.ScrollIntoView(e.NewItems[0]);
listBox.SelectedItem = e.NewItems[0];
}
}
public void Dispose()
{
if (incc != null)
incc.CollectionChanged -= incc_CollectionChanged;
}
}
}
用法:
<ListBox ItemsSource="{Binding SourceCollection}"
lb:ListBoxBehavior.ScrollOnNewItem="true"/>
更新根据下面评论中Andrej的建议,我添加了钩子来检测ListBox
的ItemsSource
中的变化.
UPDATE As per Andrej's suggestion in the comments below, I added hooks to detect a change in the ItemsSource
of the ListBox
.
这篇关于添加新项目时,如何使ListBox自动滚动?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!