WPF ListView:更改ItemsSource不会更改ListView

Dan*_*075 18 c# wpf listview

我正在使用ListView控件来显示一些数据行.有一个后台任务接收列表内容的外部更新.新接收的数据可能包含更少,更多或相同数量的项目,并且项目本身也可能已更改.

ListView.ItemsSource绑定到OberservableCollection(_itemList),以便更改_itemList应在也可以看到ListView.

_itemList = new ObservableCollection<PmemCombItem>();
_itemList.CollectionChanged += new NotifyCollectionChangedEventHandler(OnCollectionChanged);
L_PmemCombList.ItemsSource = _itemList;
Run Code Online (Sandbox Code Playgroud)

为了避免刷新完整的ListView,我将新检索的列表与当前_itemList进行简单比较,更改不相同的项目,并在必要时添加/删除项目.集合"newList"包含新创建的对象,因此替换_itemList中的项目正确地发送"刷新"通知(我可以使用OnCollectionChangedObservableCollection 的事件处理程序进行记录)

Action action = () =>
{
    for (int i = 0; i < newList.Count; i++)
    {
        // item exists in old list -> replace if changed
        if (i < _itemList.Count)
        {
            if (!_itemList[i].SameDataAs(newList[i]))
                _itemList[i] = newList[i];
        }
        // new list contains more items -> add items
        else
            _itemList.Add(newList[i]);
     }
     // new list contains less items -> remove items
     for (int i = _itemList.Count - 1; i >= newList.Count; i--)
         _itemList.RemoveAt(i);
 };
 Dispatcher.BeginInvoke(DispatcherPriority.Background, action);
Run Code Online (Sandbox Code Playgroud)

我的问题是,如果在这个循环中更改了很多项目,那么ListView就不会刷新,屏幕上的数据会保持原样...而这我不明白.

甚至更简单的版本(交换所有元素)

List<PmemCombItem> newList = new List<PmemCombItem>();
foreach (PmemViewItem comb in combList)
    newList.Add(new PmemCombItem(comb));

if (_itemList.Count == newList.Count)
    for (int i = 0; i < newList.Count; i++)
        _itemList[i] = newList[i];
else
{
    _itemList.Clear();
    foreach (PmemCombItem item in newList)
        _itemList.Add(item);
}
Run Code Online (Sandbox Code Playgroud)

工作不正常

这有什么线索吗?

UPDATE

如果我在更新所有元素后手动调用以下代码,一切正常

OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Run Code Online (Sandbox Code Playgroud)

但当然这会导致UI更新我仍想避免的所有内容.

小智 31

这是我必须要做的才能让它发挥作用.

MyListView.ItemsSource = null;
MyListView.ItemsSource = MyDataSource;
Run Code Online (Sandbox Code Playgroud)


小智 25

更改后,您可以使用以下内容刷新Listview,这样更容易

listView.Items.Refresh();
Run Code Online (Sandbox Code Playgroud)

  • 以及如何使用MVVM做到这一点? (8认同)

Sau*_*eil 6

我知道这是一个老问题,但我只是偶然发现了这个问题。我真的不想对已更新的字段使用空分配技巧或刷新。

所以,在查看 MSDN 之后,我发现了这篇文章:https : //docs.microsoft.com/en-us/dotnet/api/system.componentmodel.inotifypropertychanged?redirectedfrom= MSDN &view=netframework-4.7.2

总结一下,你只需要实现这个接口的item,它就会自动检测到这个对象可以被观察到。

public class MyItem : INotifyPropertyChanged
{
    private string status;

    public string Status
    {
        get => status;
        set
        {
            OnPropertyChanged(nameof(Status));
            status = value;
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,每次有人更改Status. 而且,在您的情况下,列表视图将在PropertyChanged事件上自动添加一个处理程序。

这并不能真正处理您的情况(添加/删除)。但为此,我建议您查看BindingList<T> https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.bindinglist-1?view=netframework-4.7.2

使用相同的模式,您的列表视图将在不使用任何技巧的情况下正确更新。


Ana*_*uza 5

您不应ItemsSourceListView每次可观察集合更改时重置。只需设置适当的绑定即可。在xaml

<ListView ItemsSource='{Binding ItemsCollection}'
 ...
</ListView>
Run Code Online (Sandbox Code Playgroud)

在代码隐藏(建议使用MVVM)属性中,将负责持有_itemList

public ObservableCollection<PmemCombItem> ItemsCollection
{
   get 
   {
      if (_itemList == null)
      {
          _itemList = new ObservableCollection<PmemCombItem>();
      }
      return _itemList;
   }
}
Run Code Online (Sandbox Code Playgroud)


更新: 有类似的帖子,很可能会回答您的问题:如何通过工作线程更新 ObservableCollection?