【问题标题】:ObservableCollection via ListCollectionView displays incorrect list of items通过 ListCollectionView 的 ObservableCollection 显示不正确的项目列表
【发布时间】:2012-01-10 18:34:23
【问题描述】:

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) =&gt; View.Refresh(); 行被移动之后 View = new ListCollectionView(Models);(或完全注释掉),事情会按预期工作。

为什么?

附:这是一个更大的难题中的一个简单部分。在这个小例子中,我意识到我不需要在 CollectionChanged 上调用 View.Refresh(); 来让 ListBox 自行更新。

【问题讨论】:

    标签: c# wpf data-binding observablecollection listcollectionview


    【解决方案1】:

    我的猜测是刷新会干扰视图的自动更新。大概视图在构造函数中也订阅了CollectionChanged,因此,如果您还在视图之前订阅了事件并调用刷新,您会在集合更改和视图自己的更新之间获得不需要的更新。

    例如

    项目 0 已删除 -> 通知事件侦听器
    => 你的处理程序:Refresh() -> 重建视图 => 项目被删除。
    => View handler: Event args say: Item X was removed -> Remove item X

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

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

    项目 0 已删除 -> 通知事件侦听器
    => 查看处理程序:事件参数说:项目 X 已删除 -> 删除项目 X
    => 你的处理程序:Refresh() -> 重建视图 => 没有改变。

    【讨论】:

    • 这听起来不错,但我想我们并不确定...为什么最后一项会被删除?
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      即使您将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 将为您更新 UI。

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

      【讨论】:

        【解决方案4】:

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

        var sortedCities  = CollectionViewSource.GetDefaultView(Models);
        

        【讨论】:

        • 你能解释一下原因吗?
        • CollectionViewSource 跟踪您的 Ilist 集合的所有视图。使用 new 将创建一个集合视图的新实例,并将其添加到 CollectionViewSource。使用 GetDefaultView 将为您提供集合的默认视图,并保护您的代码免受多个视图的影响。因此,您可以在单个视图上添加排序和过滤器。
        猜你喜欢
        • 2016-06-09
        • 1970-01-01
        • 2014-11-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-27
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多