dotnet / dotnet/wpf

[WPF] BindingListCollectionView ignores item replace

Open
#1,314 4 comments 1 reaction 1 assignee Claimed by @dipeshmsft View on GitHub
Enhancement Requested
Dominant language
C#
Stars
7.7k
Forks
1.3k
Avg merge
1d 11h
Merged PRs (30d)
61

Description

Disclaimer: I know that ObservableCollection<T> is the recommended collection type for bindings. Still, if a collection implements both INotifyCollectionChanged and IBindingList interfaces (such as ObservableBindingList<T>), then WPF prefers to handle it as a binding list (picks a BindingListCollectionView for it), which has the issues described below.

Issue Reproduction Steps:

  1. Bind a DataGrid to a BindingList<T> where T implements INotifyPropertyChanged. You can use this demo app. Just select BindingList and ObservableTestObject as list and element types, respectively.
  2. Replace an element in the bound list. In the linked app press the Item button on the toolbar to do so.
  3. As a result, the item will be replaced but the controls will not get any notification. By pressing Item again, an error occurs because a non-existing item is tried to be replaced.

Remark: If T does not implement INotifyPropertyChanged (select PlainTestObject in the app linked above), then replace works; however, since a Reset change type is raised instead of Replace the selection always jumps to the first item.

Proposed fix:

After analyzing the BindingListCollectionView source it seems that the issue can be fixed by a small change in the OnListChanged method:

case ListChangedType.ItemChanged:

////// Fix starts here - here ItemChanged refers to a Replace event (indexer set) and not a property change
if (args.PropertyDescriptor == null)
{
item = InternalList[index];
var oldItem = _cachedList[index];
_cachedList[index] = item;
forwardedArgs = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, item, oldItem, index);
break;
}
////// Fix ends here - below is the original code where ItemChange refers to a property change

if (!_itemsRaisePropertyChanged.HasValue)
{
// check whether individual items raise PropertyChanged events
// (DataRowView does)
item = InternalList[args.NewIndex];
_itemsRaisePropertyChanged = (item is INotifyPropertyChanged);
}

// if items raise PropertyChanged, we can ignore ItemChanged;
// otherwise, treat it like a Reset
if (!_itemsRaisePropertyChanged.Value)
{
goto case ListChangedType.Reset;
}
break;

Disclaimer: I could not test it.

_This issue has been moved from https://developercommunity.visualstudio.com/content/problem/651699/wpf-bindinglistcollectionview-ignores-item-replace.html
VSTS ticketId: 949137_
_These are the original issue comments:_

Visual Studio Feedback System on 7/19/2019, 02:18 PM (35 min ago):

We have directed your feedback to the appropriate engineering team for further evaluation. The team will review the feedback and notify you about the next steps.

_These are the original issue solutions:_
(no solutions)

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.