【问题标题】:Can't bind to IEnumerable implementation using yield无法使用 yield 绑定到 IEnumerable 实现
【发布时间】:2010-08-18 19:23:45
【问题描述】:

我有一个封装了一堆集合的类。我想将此类绑定到列表框以显示集合中的项目。该类实现 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>();
    }
}

【问题讨论】:

  • 我发现对我来说获得可绑定数据源的两个解决方案是实现 IList 和 IEnumerator,或者实现 IEnumerable 和 INotifyCollectionChanged。我决定让数据源实现 IEnumerable 和 INotifyCollectionChanged,因为它的实现较少,并且客户端代码无法添加项目。

标签: wpf data-binding ienumerable yield


【解决方案1】:

当您设置 ItemsSource WPF 通过CollectionView 访问它以启用排序、分组等。该视图是共享的,因此在您的情况下,每个 ItemsControl 都使用相同的 Enumerator 但它永远不会重置,因此只会出现最后一个项目。

如果您想要快照行为,有几种方法,但为每个 ItemsControl 创建一个新列表是最简单的。

如果您希望所有这些都同步并自动更新,请参阅实现 INotifyCollectionChanged 的 ObservableCollection。

您也应该使用通用的 IEnumerable 接口。

【讨论】:

  • 我还看到 WPF 使用集合视图来获取数据。为什么列表中的枚举器不会重置来自 yield 的枚举器?当你使用yield时它会创建什么样的collectionview?
  • 找到了这个讨论产量和重置的答案。谢谢你指引我正确的方向。我想我会硬着头皮实现自己的枚举器而不是使用产量。 stackoverflow.com/questions/2308163/…
  • 您可以使用 yield 并将返回类型设置为 IEnumerable 而不是 IEnumerator 并删除 IEnumerable 的实现。然后设置 DataContext 以调用该方法。
猜你喜欢
  • 2018-07-09
  • 2011-01-04
  • 1970-01-01
  • 2010-11-23
  • 2017-10-10
  • 2017-05-28
  • 1970-01-01
  • 1970-01-01
  • 2014-12-04
相关资源
最近更新 更多