WPF ListView с ItemsSource: как узнать, когда модель добавляет элемент

Вопрос: Каков «правильный» способ сообщить «модели представления» пользовательского элемента управления (файл .xaml.cs), что ListViewItem был добавлен в ListView? Обратите внимание, что этот пост посвящен другой проблеме.

Подробности:

У меня есть UserControl, который содержит ListView и DataContext:

  1. ListView имеет ItemsSource = {Binding ActionLogEntries}
  2. ActionLogEntries - это свойство ObservableCollection в DataContext.

Контекст данных добавляет элементы в ListView, когда происходят определенные события.

Но нет события ListView.ItemAdded. В ObservableCollection есть событие CollectionChanged в контексте данных, но обработчик этого события модели представления может быть вызван до того, как элемент будет добавлен в ListView, поэтому это не кажется хорошей стратегией.

К вашему сведению: это возникло, потому что, когда элементы добавляются в ListView, он не прокручивается автоматически до недавно добавленного элемента, что является поведением, которое я должен добавить. Предположительно, после этого я бы использовал ScrollIntoView.


person Oliver    schedule 15.02.2013    source источник
comment
Как насчет обработки события ItemsChanged из ItemContainerGenerator в ListView? Возможно, еще рано ...   -  person Clemens    schedule 15.02.2013
comment
Кажется, это работает. Однако аргумент события немного странный: e.Position - это генератор позиции со свойствами Index и Offset, и похоже, что вам нужно добавить index + offset, если вас интересует последний добавленный элемент. Подробности ниже.   -  person Oliver    schedule 16.02.2013


Ответы (1)


Итак, есть как минимум два способа снять шкуру с этой кошки:

  1. делать, как объяснил Клеменс в комментарии к моему вопросу
  2. сделайте как в в этом сообщении WPF Mentor

Решение 1 кажется более естественным для подписки на события, поскольку вам не нужно приводить ее; также IntelliSense не показывает члены класса реализованных интерфейсов без приведения типов, поэтому для решения 2 вы должны не забывать смотреть, какие интерфейсы реализованы, и проверять наличие там событий. Вот как выглядит подписка для каждого решения:

protected override void OnInitialized(EventArgs e)
{
    base.OnInitialized(e);

    // Solution 1, subscription:
    xActionListView.ItemContainerGenerator.ItemsChanged +=
        new ItemsChangedEventHandler(ActionLog_ItemsChanged);

    // Solution 2, subscription:
    ((INotifyCollectionChanged)xActionListView.Items).CollectionChanged += 
        new NotifyCollectionChangedEventHandler(ActionListView_CollectionChanged);
}

Но в решении 2 проще использовать аргумент события в обработчике:

// Solution 1, handler: 
private void ActionLog_ItemsChanged(object sender, ItemsChangedEventArgs e)
{
    if (e.Action == NotifyCollectionChangedAction.Add)
    {
        // Solution 1, scroll the new item into view  
        xActionListView.ScrollIntoView(
            xActionListView.Items[e.Position.Index + e.Position.Offset]);
    }      
}

// Solution 2, handler: 
private void ActionListView_CollectionChanged(
    object sender, NotifyCollectionChangedEventArgs e)
{
    if (e.Action == NotifyCollectionChangedAction.Add)
    {
        // Solution 2, scroll the new item into view  
        xActionListView.ScrollIntoView(e.NewItems[0]);
    }      
}

Похоже, что в некоторых обстоятельствах одно решение может быть более подходящим, чем другое: данные событий может быть проще использовать в одном или другом в зависимости от того, какие данные вам нужны.

person Oliver    schedule 16.02.2013