使用 ListBox
的另一种解决方案。为了实现自动滚动,您可以创建一个自定义控件!
C#
public class LoggingListBox : ListBox
{
public static readonly DependencyProperty AutoScrollProperty =
DependencyProperty.Register(
"AutoScroll",
typeof(Boolean),
typeof(LoggingListBox),
new FrameworkPropertyMetadata(
true,
FrameworkPropertyMetadataOptions.AffectsArrange |
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
AutoScroll_PropertyChanged));
[Category("Common")]
public bool AutoScroll
{
get { return (bool)GetValue(AutoScrollProperty); }
set { SetValue(AutoScrollProperty, value); }
}
private static void AutoScroll_PropertyChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
SubscribeToAutoScroll_ItemsCollectionChanged(
(LoggingListBox)d,
(bool)e.NewValue);
}
private static void SubscribeToAutoScroll_ItemsCollectionChanged(
LoggingListBox listBox, bool subscribe)
{
INotifyCollectionChanged notifyCollection =
listBox.Items.SourceCollection as INotifyCollectionChanged;
if (notifyCollection != null)
{
if (subscribe)
{
notifyCollection.CollectionChanged +=
listBox.AutoScroll_ItemsCollectionChanged;
}
else
{
notifyCollection.CollectionChanged -=
listBox.AutoScroll_ItemsCollectionChanged;
}
}
}
private void AutoScroll_ItemsCollectionChanged(
object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
int count = Items.Count;
ScrollIntoView(Items[count - 1]);
}
}
public LoggingListBox()
{
SubscribeToAutoScroll_ItemsCollectionChanged(
this, (bool)AutoScrollProperty.DefaultMetadata.DefaultValue);
}
}
XAML
以下是在XAML中使用该控件的方法:
<tools:LoggingListBox/> <!-- AutoScroll="true" by default. -->
有时候你需要指定如何访问这个控件。这完全取决于你的项目设置。
xmlns:tools="clr-namespace:MyCustomControls;assembly=MyCustomControls"
如何工作
创建自定义控件只需要使用C#代码即可。我们通过扩展ListBox
并添加一个属性AutoScroll来实现。由于它是一个依赖属性,因此它将参与WPF绑定系统,并在Visual Studio设计器中可用。
涵盖依赖属性是一个相当大的主题,但是创建自定义控件至关重要。您可以在控件作者概述或依赖属性概述中了解更多信息。
我们的目标是订阅基础项集合的集合更改事件,以便在添加新项时滚动到底部进行响应。我们必须在两个地方订阅此事件。
- 每当将
AutoScroll
设置为true时,我们都需要订阅。 AutoScroll
的值可能随时更改,我们应该能够做出相应的响应。 如果设置为false,则应指示控件停止向底部滚动,方法是取消订阅。
- 假设
AutoScroll
仅需要在编译时设置,我们需要一种在启动时订阅的方法。 这是通过使用控件的构造函数完成的。
为什么创建自定义控件
首先,我们已经尽可能简化了XAML。 我们只需要访问控件并可选地指定或绑定到AutoScroll
属性。
它符合MVVM。 我们的视图模型不需要担心AutoScroll
功能,因为它包含在控件中。 同时,视图模型可以提供一个属性,该属性绑定到AutoScroll
属性,从而实现视图和视图模型的所需解耦。
此外,我们避免了使用行为。 这意味着我们从项目中删除了两个依赖项(尽管这是这些依赖项最初被包含的唯一原因)。 我们可以安全地省略System.Windows.Interactivity和Microsoft.Expressions.Interactions的项目引用。
缺点
这种方法只有一个缺点。 基础项集合必须实现INotifyCollectionChanged
。 在大多数情况下,这不是问题。 如果您正在使用MVVM,则可能已经将项包装在ObservableCollection
中,该集合已经实现了我们所需的接口。
享受! :-)