![](/img/trans.png)
[英]Why does IEnumerable<T>.ToList<T>() return List<T> instead of IList<T>?
[英]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.