Невозможно привязать к реализации IEnumerable с помощью yield - PullRequest
2 голосов
/ 18 августа 2010

У меня есть класс, инкапсулирующий кучу коллекций.Я хотел бы связать этот класс со списком для отображения элементов в коллекциях.Класс реализует 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>();
    }
}

1 Ответ

2 голосов
/ 19 августа 2010

Когда вы устанавливаете ItemSource WPF обращается к нему через CollectionView , чтобы включить сортировку, группировку e.t.c. Представление является общим, поэтому в вашем случае каждый ItemsControl использует один и тот же перечислитель, но он никогда не сбрасывается, поэтому отображается только последний элемент.

Если вам нужно поведение моментального снимка, есть несколько методов, но создание нового списка для каждого ItemsControl является самым простым.

Если вы хотите, чтобы все они были синхронизированы и обновлены автоматически, см. ObservableCollection , которая реализует INotifyCollectionChanged .

Возможно, вам также следует использовать общий интерфейс IEnumerable.

...