Ответ 1
Это то, что я должен был сделать, чтобы заставить его работать.
MyListView.ItemsSource = null;
MyListView.ItemsSource = MyDataSource;
Я использую элемент управления ListView
для отображения некоторых строк данных. Существует фоновая задача, которая получает внешние обновления для содержимого списка. Недавно полученные данные могут содержать меньше, больше или одинаковое количество элементов, а также сами элементы могут быть изменены.
ListView.ItemsSource
привязан к OberservableCollection
(_itemList), так что изменения в _itemList должны быть видны также в ListView
.
_itemList = new ObservableCollection<PmemCombItem>();
_itemList.CollectionChanged += new NotifyCollectionChangedEventHandler(OnCollectionChanged);
L_PmemCombList.ItemsSource = _itemList;
Чтобы избежать обновления полного списка ListView, я делаю простое сравнение недавно полученного списка с текущим _itemList, изменяя элементы, которые не являются одинаковыми, и при необходимости добавьте/удалите элементы. Коллекция "newList" содержит вновь созданные объекты, поэтому замена элемента в _itemList корректно отправляет уведомление "Обновить" (которое я могу зарегистрировать с помощью обработчика событий OnCollectionChanged
ObservableCollection`)
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);
Моя проблема в том, что если в этом цикле меняются многие элементы, 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);
}
работает неправильно
Какой-нибудь ключ к этому?
UPDATE
Если после обновления всех элементов я вызываю следующий код вручную, все работает отлично
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Но, конечно, это заставляет пользовательский интерфейс обновлять все, чего я все еще хочу избежать.
Это то, что я должен был сделать, чтобы заставить его работать.
MyListView.ItemsSource = null;
MyListView.ItemsSource = MyDataSource;
После изменения вы можете использовать следующее, чтобы обновить Listview, это проще
listView.Items.Refresh();
Вы не должны reset ItemsSource
of ListView
каждый раз, когда изменяется наблюдаемая коллекция. Просто установите правильное связывание, которое сделает ваш трюк. В xaml
:
<ListView ItemsSource='{Binding ItemsCollection}'
...
</ListView>
И в коде (подскажите использовать MVVM) свойство, которое будет нести ответственность за сохранение _itemList
:
public ObservableCollection<PmemCombItem> ItemsCollection
{
get
{
if (_itemList == null)
{
_itemList = new ObservableCollection<PmemCombItem>();
}
return _itemList;
}
}
Я нашел способ сделать это. Это не очень здорово, но оно работает.
YourList.ItemsSource = null;
// Update the List containing your elements (lets call it x)
YourList.ItemsSource = x;
это должно обновить ваш ListView
(он работает для моего UAP:))
Я знаю, что старый вопрос, но я просто наткнулся на этот вопрос. Я действительно не хотел использовать трюк с нулевым присваиванием или обновление только для поля, которое было обновлено.
Итак, после просмотра MSDN я обнаружил эту статью: https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.inotifypropertychanged?redirectedfrom=MSDN&view=netframework-4.7.2
Подводя итог, вам просто нужен элемент для реализации этого интерфейса, и он автоматически обнаружит, что этот объект можно наблюдать.
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));
}
}
Таким образом, событие будет вызываться каждый раз, когда кто-то меняет Status
. И, в вашем случае, просмотр списка автоматически добавит обработчик в событие PropertyChanged
.
Это действительно не решает проблему в вашем случае (добавить/удалить). Но для этого я бы посоветовал вам взглянуть на BindingList<T>
https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.bindinglist-1?view=netframework-4.7.2.
Используя тот же шаблон, ваш список будет обновляться должным образом, без каких-либо хитростей.