通过ListCollectionView的ObservableCollection显示不正确的项列表

时间:2012-01-10 18:34:24

标签: c# wpf data-binding observablecollection listcollectionview

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个元素。单击该按钮时,将删除ObservableCollection的第一个元素。但是,ListBox现在只显示第二个和第三个。看起来已经删除了第1和第4个。

如果在 Models.CollectionChanged += (s, e) => View.Refresh();之后移动了>(或完全注释掉了),那么事情就会按预期进行。

为什么?

P.S。这是一个更大的拼图的简单部分。在这个小例子中,我意识到我不需要在CollectionChanged上调用View = new ListCollectionView(Models);来让ListBox自行更新。

4 个答案:

答案 0 :(得分:1)

我的猜测是刷新会干扰视图的自动更新。据推测,视图也在构造函数中订阅了CollectionChanged,因此如果您还在视图执行之前订阅了该事件并调用了刷新,则会在集合更改和视图自己的更新之间进行不必要的更新。

e.g。

删除了第0项 - &gt;通知事件监听器
 =&GT;你的处理程序:Refresh() - &gt;重建视图=&gt;物品被移除。
 =&GT;查看处理程序:事件参数说:项目X已删除 - &gt;删除项目X

这仍然无法解释为什么第一个和最后一个项目被删除但对我来说似乎是合理的。

如果订阅在视图实例化之后:

删除了第0项 - &gt;通知事件监听器
 =&GT;查看处理程序:事件参数说:项目X已删除 - &gt;删除项目X
 =&GT;你的处理程序:Refresh() - &gt;重建视图=&gt;什么都没有改变。

答案 1 :(得分:1)

似乎是ListView刷新的问题。如果将labe / TextBlock绑定到ListView.Items.Count,您将看到该列表仍有3个项目(首次删除后)。

答案 2 :(得分:1)

即使您将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);

答案 3 :(得分:0)

尽量不要使用new关键字来定义listCollectionView的新实例。请改用以下内容。

var sortedCities  = CollectionViewSource.GetDefaultView(Models);