我确信这有很好的解释。我猜它与感冒有关,遗漏了一些明显的东西......
我有一个简单的窗口:
<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
中不会显示任何内容。它只是空白。
为什么会这样?
答案 0 :(得分:4)
Using Collection Objects as a Binding Source:
您可以枚举任何实现IEnumerable接口的集合。但是,要设置动态绑定以便集合中的插入或删除自动更新UI,集合必须实现INotifyCollectionChanged接口。此接口公开了一个事件,只要底层集合发生更改,就必须引发该事件。
答案 1 :(得分:2)
objects.ToList()
将在每个Button-Click上创建一个新列表。这可能是列表刷新自己的暗示?
我猜这里......但是当你NotifyPropertyChanged时,那么框架可能会检查该属性是否确实发生了变化(它不是return objects
情况下 - 它仍然是相同的列表)
答案 2 :(得分:2)
如果在属性上引发PropertyChanged
事件,绑定将检查属性的值是否已更改,并刷新目标(如果已更改)。由于Objects
是引用类型,因此只有在将其分配给新实例时才会更改其值 - 这是使用ToList()
或ToArray()
的实例。
换句话说,当你的代码引发PropertyChanged
时,你并没有声明列表的内容已经改变,你断言该属性包含一个新列表。绑定检查源上的属性与目标上的属性,并且不同意。
这就是您应该使用ObservableCollection<T>
或其他一些实现INotifyCollectionChanged
的集合的原因。如果绑定到实现INotifyCollectionChanged
的属性,绑定将侦听两个PropertyChanged
事件(如果创建新集合并更改属性的值,则会引发)和CollectionChanged
(在项目被添加到集合中或从集合中删除。
另请注意,将基础集合更改为ObservableCollection<T>
是不够的。您必须更改您正在公开的属性的类型。绑定不会尝试侦听IEnumerable<T>
属性上的事件,因为该接口不会公开这些事件。
答案 3 :(得分:0)
这是最常见的,因为IEnumerable是Linq查询的结果,实际类型与简单的List&lt;&gt;完全不同。或集合&lt;&gt;。会发生的是它(Linq)构建“查询”的逻辑表示但不立即运行它,而是在请求值并且每个值yielding时运行它。这是Linq的基本概念之一。 Linq的不同风格可能会选择在封面下以不同的方式实现它,但概念是相同的。
实际上没关系,我应该在回答时更仔细地阅读代码;我认为您的代码没有任何方式,因为您只是实例化List。但是,ListBox永远不会直接公开项目,而是将它们包装在ICollectionView中。 ICollectionView可能与此有关,如果类型被视为IEnumerable,则选择延迟加载项目。不过不确定。它也可能取决于ObjectModel
的内部结构......虽然可能没有,因为这不会受到ToList()
的调用的影响。