ObservableCollection через ListCollectionView отображает неверный список элементов - PullRequest
3 голосов
/ 10 января 2012

C #:

public partial class MainWindow : Window
{
    private readonly ViewModel vm;

    public MainWindow()
    {
        InitializeComponent();
        vm = new ViewModel();

        DataContext = vm;
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        vm.Models.RemoveAt(0);
    }
}

public class ViewModel
{
    public ObservableCollection<Model> Models { get; set; }
    public ListCollectionView View { get; set; }

    public ViewModel()
    {
        Models = new ObservableCollection<Model>()
        {
            new Model() { Name = "Gordon Freeman" },
            new Model() { Name = "Isaac Kleiner" },
            new Model() { Name = "Eli Vance" },
            new Model() { Name = "Alyx Vance" },
        };

        Models.CollectionChanged += (s, e) => View.Refresh();
        View = new ListCollectionView(Models);
    }
}

public class Model
{
    public string Name { get; set; }

    public override string ToString()
    {
        return Name;
    }
}

XAML:

<StackPanel>
    <ListBox ItemsSource="{Binding Path=View}" />
    <Button Click="Button_Click">Click</Button>
</StackPanel>

ObservableCollection содержит 4 элемента, а ListBox отображает все 4, как и ожидалось. Когда кнопка нажата, 1-й элемент ObservableCollection удаляется. Однако ListBox теперь отображает только 2-й и 3-й. Казалось бы, 1-й и 4-й были удалены.

Если строка Models.CollectionChanged += (s, e) => View.Refresh(); перемещена после View = new ListCollectionView(Models); (или полностью закомментирована), все работает как положено.

Почему?

P.S. Это простая часть большой головоломки. В этом небольшом примере я понимаю, что мне не нужно вызывать View.Refresh(); на CollectionChanged, чтобы ListBox обновлялся сам.

Ответы [ 4 ]

1 голос
/ 08 марта 2017

Даже если вы заключаете свой класс Model в ObservableCollection, вам все равно нужно реализовать для него INotifyPropertyChanged. Я ударился головой об эту проблему достаточно много раз, и теперь я не забываю начинать устранение неполадок с самого низкого строительного блока, то есть Model.

public class Model : INotifyPropertyChanged
{
    #region INotify Implementation
    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
    #endregion

    public string _Name;
    public string Name {
        get { return _Name;  }
        set { _Name = value; NotifyPropertyChanged("Name"); }

    }
    public override string ToString()
    {
        return Name;
    }
}

Кроме того, в вашем ViewModel, когда INotifyPropertyChanged реализован правильно, вам не нужны следующие строки. INotifyPropertyChanged обновит ваш пользовательский интерфейс.

    Models.CollectionChanged += (s, e) => View.Refresh();
    View = new ListCollectionView(Models);
1 голос
/ 10 января 2012

Кажется, проблема с обновлением ListView. Если вы привяжете labe / TextBlock к ListView.Items.Count, вы увидите, что список все еще содержит 3 элемента (после первого удаления).

1 голос
/ 10 января 2012

Я предполагаю, что обновление мешает автоматическому обновлению представлением. Предположительно, представление также подписывается на CollectionChanged в конструкторе, поэтому, если вы также подпишетесь на событие до того, как представление сделает это, и вызовите refresh, вы получите нежелательное обновление между изменением коллекции и собственным обновлением представления.

, например

Элемент 0 удален -> Уведомить слушателей события
=> Ваш обработчик: Refresh () -> Перестроить представление => Элемент удален.
=> Просмотреть обработчик: Аргументы события говорят: Элемент X был удален -> Удалить элемент X

Это все еще не объясняет, почему первый и последний элементы удаляются, но мне кажется разумным.

Если подписка идет после создания экземпляра представления:

Элемент 0 удален -> Уведомить слушателей события
=> Просмотреть обработчик: Аргументы события говорят: Элемент X был удален -> Удалить элемент X
=> Ваш обработчик: Refresh () -> Пересмотреть представление => Ничего не изменилось.

0 голосов
/ 18 декабря 2013

Старайтесь не использовать новое ключевое слово для определения нового экземпляра listCollectionView. Вместо этого используйте следующее.

var sortedCities  = CollectionViewSource.GetDefaultView(Models);
...