无法使用yield绑定到IEnumerable实现
我有一个类封装了一堆集合。我想将此类绑定到列表框以显示集合中的项目。该类实现 IEnumerable。当显示列表框时,我期望调用 IEnumerable.GetEnumerator 方法。然而,当 GetEnumerator 方法使用yield 关键字时,情况并非如此。但是,如果我从 List 集合返回枚举器,它会正常工作,每次显示窗口时都会调用 GetEnumerator 方法。
List集合的枚举器有什么神奇之处??? 允许 WPF 项目控件获取快照(不需要更新)的正确接口是什么? IList 是可以使用的吗?
下面的示例代码在每次打开新窗口时添加时间戳。但是,列表框永远不会显示多个时间戳,这是第一次(也是唯一一次)调用 GetEnumerator 时的时间戳数。计数增加,因此添加时间戳。更改 GetEnumerator 方法以返回列表集合的枚举器将导致每次打开新窗口时都会调用 GetEnumerator 方法。
XAML:
<Window x:Class="YieldTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<StackPanel>
<Button Content="Open" Click="Button_Click" />
<TextBlock Text="{Binding Path=Count}" />
<ListBox ItemsSource="{Binding}" />
</StackPanel>
</Window>
隐藏代码:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows;
namespace YieldTest
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Window1 window1 = new Window1();
window1.DataContext = _timeStamps;
_timeStamps.Add(DateTime.Now);
window1.Show();
}
private static TimeStamps _timeStamps = new TimeStamps();
}
public class TimeStamps : IEnumerable
{
public void Add(DateTime time1)
{
_timeStamps.Add(time1);
}
public int Count { get { return _timeStamps.Count; } }
public IEnumerator GetEnumerator()
{
Debug.WriteLine("GetEnumerator");
// Returning the enumerator of _timeStamps will result in
// GetEnumerator called every time a new window is opened,
// which is the expected result
// return _timeStamps.GetEnumerator();
// Using yield will result in GetEnumerator is called only
// one time for the first window opened. This means that
// newer windows will have stale data.
foreach (DateTime timeStamp in _timeStamps)
{
yield return timeStamp;
}
}
private List<DateTime> _timeStamps = new List<DateTime>();
}
}
I have a class encapsulating a bunch of collections. I would like to bind this class to a listbox to display the items in the collections. The class implements IEnumerable. When the listbox is displayed I am expecting the IEnumerable.GetEnumerator method to be called. It is however not when the GetEnumerator method uses the yield keyword. However, if I would return the enumerator from a List collection it would work fine, the GetEnumerator method is called each time a window is displayed.
What is so magical about the enumerator of a List collection???
What is the correct interface to implement to allow a WPF itemscontrol to get snapshots (updates not needed)??? Is IList the one to use???
Below sample code adds a timestamp everytime a new window is opened. However, the listbox never shows more than one timestamp, which is the number of timestamps the first (and only time) GetEnumerator is called. Count get increased so timestamps are added. Changing GetEnumerator method to return the enumerator of the list collection will result in that GetEnumerator method gets called each time a new window is opened.
XAML:
<Window x:Class="YieldTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<StackPanel>
<Button Content="Open" Click="Button_Click" />
<TextBlock Text="{Binding Path=Count}" />
<ListBox ItemsSource="{Binding}" />
</StackPanel>
</Window>
Code behind:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows;
namespace YieldTest
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Window1 window1 = new Window1();
window1.DataContext = _timeStamps;
_timeStamps.Add(DateTime.Now);
window1.Show();
}
private static TimeStamps _timeStamps = new TimeStamps();
}
public class TimeStamps : IEnumerable
{
public void Add(DateTime time1)
{
_timeStamps.Add(time1);
}
public int Count { get { return _timeStamps.Count; } }
public IEnumerator GetEnumerator()
{
Debug.WriteLine("GetEnumerator");
// Returning the enumerator of _timeStamps will result in
// GetEnumerator called every time a new window is opened,
// which is the expected result
// return _timeStamps.GetEnumerator();
// Using yield will result in GetEnumerator is called only
// one time for the first window opened. This means that
// newer windows will have stale data.
foreach (DateTime timeStamp in _timeStamps)
{
yield return timeStamp;
}
}
private List<DateTime> _timeStamps = new List<DateTime>();
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
当您设置 ItemsSource WPF 时,它会通过 CollectionView< /a> 启用排序、分组等视图是共享的,因此在您的情况下,每个 ItemsControl 都使用相同的枚举器,但它永远不会重置,因此只显示最后一个项目。
如果您想要快照行为,有几种方法,但为每个 ItemsControl 创建一个新列表是最简单的。
如果您希望所有这些都同步并自动更新,请参阅 ObservableCollection 它实现了 INotifyCollectionChanged。
您可能还应该使用通用 IEnumerable 接口。
When you set the ItemsSource WPF accesses it through a CollectionView to enable sorting, grouping e.t.c. The view is shared so in your case each ItemsControl is using the same Enumerator but it is never reset so only the last item appears.
If you want Snapshotting behaviour there are a few methods but creating a new list for each ItemsControl is the simplest.
If you want all of them to be in sync and update automatically see ObservableCollection which implements INotifyCollectionChanged.
You should probably use the generic IEnumerable interface as well.