web-dev-qa-db-ja.com

WPF ListView:ItemsSourceを変更してもListViewは変更されません

ListViewコントロールを使用して、いくつかのデータ行を表示しています。リストのコンテンツへの外部更新を受信するバックグラウンドタスクがあります。新しく受信したデータには、より少ない、より多い、または同じ数のアイテムが含まれている場合があり、アイテム自体が変更されている場合もあります。

ListView.ItemsSourceOberservableCollection(_itemList)にバインドされているので、_itemListへの変更はListViewでも表示されるはずです。

_itemList = new ObservableCollection<PmemCombItem>();
_itemList.CollectionChanged += new NotifyCollectionChangedEventHandler(OnCollectionChanged);
L_PmemCombList.ItemsSource = _itemList;

ListView全体の更新を避けるために、新しく取得したリストと現在の_itemListを単純に比較し、同じではないアイテムを変更し、必要に応じてアイテムを追加/削除します。コレクション「newList」には新しく作成されたオブジェクトが含まれているため、_itemListのアイテムを置き換えると、「Refresh」通知が正しく送信されます(ObservableCollection`のイベントハンドラーOnCollectionChangedを使用してログを記録できます)

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));

しかし、もちろん、これにより、UIは、私がまだ避けたいすべてを更新します。

16
Danny1075

これが機能するために私がしなければならなかったことです。

MyListView.ItemsSource = null;
MyListView.ItemsSource = MyDataSource;
29
Xopher

変更後、次を使用してリストビューを更新できます。より簡単です

listView.Items.Refresh();
24
matthewpepper

私はそれが古い質問であることを知っていますが、私はこの問題につまずいただけです。更新されたフィールドのみに対して、null割り当てトリックまたはリフレッシュを使用したくありませんでした。

だから、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

同じパターンを使用すると、トリックを使用せずにリストビューが適切に更新されます。

3
Sauleil

私はそれを行う方法を見つけました。それは本当にそれほど素晴らしいことではありませんが、うまくいきます。

YourList.ItemsSource = null;
// Update the List containing your elements (lets call it x)
YourList.ItemsSource = x;

これでListViewが更新されます(私のUAPで機能します:))