Другое решение, использующее ListBox
. Для реализации автоматической прокрутки вы можете создать собственный элемент управления!
C #
public class LoggingListBox : ListBox
{
///<summary>
///Define the AutoScroll property. If enabled, causes the ListBox to scroll to
///the last item whenever a new item is added.
///</summary>
public static readonly DependencyProperty AutoScrollProperty =
DependencyProperty.Register(
"AutoScroll",
typeof(Boolean),
typeof(LoggingListBox),
new FrameworkPropertyMetadata(
true, //Default value.
FrameworkPropertyMetadataOptions.AffectsArrange |
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
AutoScroll_PropertyChanged));
/// <summary>
/// Gets or sets whether or not the list should scroll to the last item
/// when a new item is added.
/// </summary>
[Category("Common")] //Indicate where the property is located in VS designer.
public bool AutoScroll
{
get { return (bool)GetValue(AutoScrollProperty); }
set { SetValue(AutoScrollProperty, value); }
}
/// <summary>
/// Event handler for when the AutoScroll property is changed.
/// This delegates the call to SubscribeToAutoScroll_ItemsCollectionChanged().
/// </summary>
/// <param name="d">The DependencyObject whose property was changed.</param>
/// <param name="e">Change event args.</param>
private static void AutoScroll_PropertyChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
SubscribeToAutoScroll_ItemsCollectionChanged(
(LoggingListBox)d,
(bool)e.NewValue);
}
/// <summary>
/// Subscribes to the list items' collection changed event if AutoScroll is enabled.
/// Otherwise, it unsubscribes from that event.
/// For this to work, the underlying list must implement INotifyCollectionChanged.
///
/// (This function was only creative for brevity)
/// </summary>
/// <param name="listBox">The list box containing the items collection.</param>
/// <param name="subscribe">Subscribe to the collection changed event?</param>
private static void SubscribeToAutoScroll_ItemsCollectionChanged(
LoggingListBox listBox, bool subscribe)
{
INotifyCollectionChanged notifyCollection =
listBox.Items.SourceCollection as INotifyCollectionChanged;
if (notifyCollection != null)
{
if (subscribe)
{
//AutoScroll is turned on, subscribe to collection changed events.
notifyCollection.CollectionChanged +=
listBox.AutoScroll_ItemsCollectionChanged;
}
else
{
//AutoScroll is turned off, unsubscribe from collection changed events.
notifyCollection.CollectionChanged -=
listBox.AutoScroll_ItemsCollectionChanged;
}
}
}
/// <summary>
/// Event handler called only when the ItemCollection changes
/// and if AutoScroll is enabled.
/// </summary>
/// <param name="sender">The ItemCollection.</param>
/// <param name="e">Change event args.</param>
private void AutoScroll_ItemsCollectionChanged(
object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
int count = Items.Count;
ScrollIntoView(Items[count - 1]);
}
}
/// <summary>
/// Constructor a new LoggingListBox.
/// </summary>
public LoggingListBox()
{
//Subscribe to the AutoScroll property's items collection
//changed handler by default if AutoScroll is enabled by default.
SubscribeToAutoScroll_ItemsCollectionChanged(
this, (bool)AutoScrollProperty.DefaultMetadata.DefaultValue);
}
}
1010 * 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 из ссылок проекта.
1062 * Недостатки *
У этого подхода есть только один недостаток. Основная коллекция элементов должна реализовывать INotifyCollectionChanged
. В большинстве случаев это не проблема. Если вы используете MVVM, вы, вероятно, уже завернули свои элементы в ObservableCollection
, который уже реализует наш требуемый интерфейс.
Наслаждайтесь! : -)