Почему IEnumerable <T>требует вызова ToList для обновления списка? - PullRequest
3 голосов
/ 16 сентября 2010

Я уверен, что этому есть хорошее объяснение. Я предполагаю, что это как-то связано с простудой и отсутствием чего-то очевидного ...

У меня есть простое окно:

<Window x:Class="WpfIdeas.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:w="clr-namespace:WpfIdeas"
    Title="Window1" Height="300" Width="315">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="20"></RowDefinition>
            <RowDefinition Height="*"></RowDefinition>
        </Grid.RowDefinitions>
        <Button Grid.Row="0" x:Name="btnAddObject" Click="btnAddObject_Click">Add Object</Button>
        <ListView Grid.Row="1"  ItemsSource="{Binding Objects}">
        </ListView>
    </Grid>
</Window>

код за окном:

using System.Windows;

namespace WpfIdeas
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
            DataContext = new ObjectVM();
        }

        private void btnAddObject_Click(object sender, RoutedEventArgs e)
        {
            (DataContext as ObjectVM).AddObject();
        }
    }
}

И его DataContext имеет следующий класс:

class ObjectVM : INotifyPropertyChanged
{
    private readonly List<ObjectModel> objects = new List<ObjectModel>();

    //public IEnumerable<ObjectModel> Objects { get { return objects } } //doesn't work
    public IEnumerable<ObjectModel> Objects { get { return objects.ToList() } } //works

    private Random r = new Random();

    public void AddObject()
    {
        ObjectModel o = new ObjectModel(r);
        objects.Add(o);
        if(PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs("Objects"));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

Класс ObjectModel на самом деле является структурой, которая генерирует 14-символьную строку при ее создании. Это ToString() метод просто выводит эту строку.

Поскольку код выше, когда я нажимаю кнопку «Добавить объект», появляется новая строка в ListView.

Однако, если я удаляю вызов ToList() в свойстве Objects, в ListView ничего не отображается. Это просто остается пустым.

Почему это?

Ответы [ 4 ]

4 голосов
/ 16 сентября 2010

Использование объектов коллекции в качестве источника привязки :

Вы можете перечислить любую коллекцию, которая реализует интерфейс IEnumerable. Однако для настройки динамических привязок, чтобы вставки или удаления в коллекции автоматически обновляли пользовательский интерфейс, коллекция должна реализовывать интерфейс INotifyCollectionChanged. Этот интерфейс предоставляет событие, которое должно вызываться при изменении базовой коллекции.

2 голосов
/ 16 сентября 2010

Если вы вызываете событие PropertyChanged для свойства, привязка проверяет, изменилось ли значение свойства, и обновляет цель, если оно имело.Поскольку Objects является ссылочным типом, его значение изменяется только в том случае, если вы присваиваете его новому экземпляру, что делает ToList() или ToArray().

Другими словами, когда ваш код повышается PropertyChanged, вы не утверждаете, что содержимое списка изменилось, вы утверждаете, что свойство содержит новый список.Привязка проверяет свойство источника по отношению к свойству цели и не соглашается.

Вот почему вы должны использовать ObservableCollection<T> или другую коллекцию, которая реализует INotifyCollectionChanged.Если вы привязываете к свойству, которое реализует INotifyCollectionChanged, то привязка будет прослушивать как события PropertyChanged (генерируемые при создании новой коллекции и изменении значения свойства), так и CollectionChanged (возникающие при добавлении или удалении элементовиз коллекции).

Также обратите внимание, что недостаточно изменить основную коллекцию на ObservableCollection<T>.Вы должны изменить тип свойства, которое вы выставляете.Привязка не будет пытаться прослушивать события в свойстве IEnumerable<T>, поскольку эти события не отображаются этим интерфейсом.

2 голосов
/ 16 сентября 2010

objects.ToList() будет создавать новый список при каждом нажатии кнопки.Это, вероятно, сигнал для обновления самого списка?

Я предполагаю, что здесь ... Но когда вы NotifyPropertyChanged, то фреймворк может проверить, действительно ли свойство изменилось (оно не изменилосьслучай return objects - это все тот же список).

0 голосов
/ 16 сентября 2010

Чаще всего это происходит потому, что IEnumerable является результатом запроса Linq, а фактический тип является чем-то совершенно отличным от простого List <> или Collection <>.Что происходит, так это то, что он (Linq) создает логическое представление «запроса», но не запускает его сразу, вместо этого запускает его, когда запрашиваются значения, и дает каждое значение.Это одна из основных концепций Linq.Различные варианты Linq могут по-разному реализовать его под прикрытием, но концепция одна и та же.

На самом деле не имеет значения, я должен прочитать код более внимательно, когда отвечу;Я не думаю, что с вашим кодом что-то случится, потому что вы просто создаете экземпляр List.Однако ListBox никогда не предоставляет элементы напрямую, вместо этого оборачивая их в ICollectionView.ICollectionView может иметь какое-то отношение к этому, выбирая ленивую загрузку элементов, если тип рассматривается как IEnumerable.Не уверен, хотя.Это также может зависеть от внутренней структуры ObjectModel ... хотя, вероятно, нет, так как это не повлияет на вызов ToList().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...