繁体   English   中英

为什么IEnumerable <T> 需要调用ToList才能更新listview?

[英]Why does an IEnumerable<T> require a call to ToList to update the listview?

我相信这有很好的解释。 我猜这与我感冒有关,遗漏了一些明显的东西......

我有一个简单的窗口:

<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出现一个新字符串。

但是,如果我删除Objects属性中的ToList()调用,则ListView不会显示任何内容。 它只是空白。

为什么是这样?

使用集合对象作为绑定源

您可以枚举实现IEnumerable接口的任何集合。 但是,要设置动态绑定以便集合中的插入或删除自动更新UI,集合必须实现INotifyCollectionChanged接口。 此接口公开了一个事件,只要底层集合发生更改,就必须引发该事件。

objects.ToList()将在每个Button-Click上创建一个新列表。 这可能是列表刷新自己的暗示?

我猜这里......但是当你NotifyPropertyChanged时,框架可能会检查该属性是否确实发生了变化(它不是在return objects情况下 - 它仍然是相同的列表)。

如果在PropertyChanged上引发PropertyChanged事件,绑定将检查属性的值是否已更改,并刷新目标(如果已更改)。 由于Objects是一个引用类型,因此只有在将其赋值给新实例时才会更改它的值 - 这就是使用ToList()ToArray()实例。

换句话说,当您的代码引发PropertyChanged ,您并未断言列表的内容已更改,您声明该属性包含新列表。 绑定检查源上的属性与目标上的属性,并且不同意。

这就是您应该使用ObservableCollection<T>或其他实现INotifyCollectionChanged集合的INotifyCollectionChanged 如果绑定到实现INotifyCollectionChanged的属性,绑定将侦听PropertyChanged事件(如果创建新集合并更改属性的值则引发)和CollectionChanged (在项目添加到集合或从集合中删除时引发)。

另请注意,将基础集合更改为ObservableCollection<T>是不够的。 您必须更改您正在公开的属性的类型。 绑定不会尝试侦听IEnumerable<T>属性上的事件,因为该接口不会公开这些事件。

这是最常见的,因为IEnumerable是Linq查询的结果,实际类型与简单的List <>或Collection <>完全不同。 会发生的是它(Linq)构建“查询”的逻辑表示,但不立即运行它,而是在请求值时运行它并产生每个值。 这是Linq的基本概念之一。 Linq的不同风格可能会选择在封面下以不同的方式实现它,但概念是相同的。

实际上没关系,我应该在回答时更仔细地阅读代码; 我认为您的代码没有任何方式,因为您只是实例化List。 但是,ListBox永远不会直接公开项目,而是将它们包装在ICollectionView中。 ICollectionView可能与此有关,如果类型被视为IEnumerable,则选择延迟加载项目。 不过不确定。 它也可能依赖于ObjectModel的内部结构......虽然可能不是,因为它不会受到对ToList()的调用的影响。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM