diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/Data/CompositeCollectionView.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/Data/CompositeCollectionView.cs
index ffa95ce1fa3..86d7314870c 100644
--- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/Data/CompositeCollectionView.cs
+++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/Data/CompositeCollectionView.cs
@@ -384,9 +384,16 @@ protected override void ProcessCollectionChanged(NotifyCollectionChangedEventArg
{
ValidateCollectionChangedEventArgs(args);
+ bool isRangeAction = IsCollectionChangedRangeAction(args);
+
bool moveCurrencyOffDeletedElement = false;
- switch (args.Action)
+ // If we have a range operation, treat it as a refresh for now
+ NotifyCollectionChangedAction effectiveAction = isRangeAction
+ ? NotifyCollectionChangedAction.Reset
+ : args.Action;
+
+ switch (effectiveAction)
{
case NotifyCollectionChangedAction.Add:
case NotifyCollectionChangedAction.Remove:
@@ -608,7 +615,7 @@ protected override void ProcessCollectionChanged(NotifyCollectionChangedEventArg
case NotifyCollectionChangedAction.Reset:
{
- _traceLog?.Add("ProcessCollectionChanged action = {0}", args.Action);
+ _traceLog?.Add("ProcessCollectionChanged action = {0}", effectiveAction);
if (_collection.Count != 0)
{
@@ -1430,23 +1437,23 @@ private void ValidateCollectionChangedEventArgs(NotifyCollectionChangedEventArgs
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
- if (e.NewItems.Count != 1)
- throw new NotSupportedException(SR.RangeActionsNotSupported);
+ if (e.NewItems.Count < 1)
+ throw new NotSupportedException(SR.UnexpectedCollectionChangeAction);
break;
case NotifyCollectionChangedAction.Remove:
- if (e.OldItems.Count != 1)
- throw new NotSupportedException(SR.RangeActionsNotSupported);
+ if (e.OldItems.Count < 1)
+ throw new NotSupportedException(SR.UnexpectedCollectionChangeAction);
break;
case NotifyCollectionChangedAction.Replace:
- if (e.NewItems.Count != 1 || e.OldItems.Count != 1)
- throw new NotSupportedException(SR.RangeActionsNotSupported);
+ if (e.NewItems.Count < 1 || e.OldItems.Count < 1)
+ throw new NotSupportedException(SR.UnexpectedCollectionChangeAction);
break;
case NotifyCollectionChangedAction.Move:
- if (e.NewItems.Count != 1)
- throw new NotSupportedException(SR.RangeActionsNotSupported);
+ if (e.NewItems.Count < 1)
+ throw new NotSupportedException(SR.UnexpectedCollectionChangeAction);
if (e.NewStartingIndex < 0)
throw new InvalidOperationException(SR.CannotMoveToUnknownPosition);
break;
@@ -1459,6 +1466,15 @@ private void ValidateCollectionChangedEventArgs(NotifyCollectionChangedEventArgs
}
}
+ private bool IsCollectionChangedRangeAction(NotifyCollectionChangedEventArgs e)
+ {
+ return
+ (e.Action == NotifyCollectionChangedAction.Add && e.NewItems.Count > 1) ||
+ (e.Action == NotifyCollectionChangedAction.Remove && e.OldItems.Count > 1) ||
+ (e.Action == NotifyCollectionChangedAction.Replace && (e.OldItems.Count > 1 || e.NewItems.Count > 1)) ||
+ (e.Action == NotifyCollectionChangedAction.Move && (e.OldItems.Count > 1 || e.NewItems.Count > 1));
+ }
+
///
/// Helper to raise a PropertyChanged event />).
///
diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/ItemContainerGenerator.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/ItemContainerGenerator.cs
index 8eb991d009e..85554796a1c 100644
--- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/ItemContainerGenerator.cs
+++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/ItemContainerGenerator.cs
@@ -2388,35 +2388,46 @@ private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs
switch (args.Action)
{
case NotifyCollectionChangedAction.Add:
+
+ // Treat range operations as refresh operations, for minimum support.
+ // This can be expanded later with a better implementation, if needed.
if (args.NewItems.Count != 1)
- throw new NotSupportedException(SR.RangeActionsNotSupported);
- OnItemAdded(args.NewItems[0], args.NewStartingIndex);
+ OnRefresh();
+ else
+ OnItemAdded(args.NewItems[0], args.NewStartingIndex);
break;
case NotifyCollectionChangedAction.Remove:
if (args.OldItems.Count != 1)
- throw new NotSupportedException(SR.RangeActionsNotSupported);
- OnItemRemoved(args.OldItems[0], args.OldStartingIndex);
+ OnRefresh();
+ else
+ OnItemRemoved(args.OldItems[0], args.OldStartingIndex);
break;
case NotifyCollectionChangedAction.Replace:
- // Don't check arguments if app targets 4.0, for compat ( 726682)
+ // Don't check arguments if app targets 4.0, for compat (726682)
if (!FrameworkCompatibilityPreferences.TargetsDesktop_V4_0)
{
if (args.OldItems.Count != 1)
- throw new NotSupportedException(SR.RangeActionsNotSupported);
+ OnRefresh();
+ else
+ OnItemReplaced(args.OldItems[0], args.NewItems[0], args.NewStartingIndex);
}
- OnItemReplaced(args.OldItems[0], args.NewItems[0], args.NewStartingIndex);
+ else
+ OnItemReplaced(args.OldItems[0], args.NewItems[0], args.NewStartingIndex);
break;
case NotifyCollectionChangedAction.Move:
- // Don't check arguments if app targets 4.0, for compat ( 726682)
+ // Don't check arguments if app targets 4.0, for compat (726682)
if (!FrameworkCompatibilityPreferences.TargetsDesktop_V4_0)
{
if (args.OldItems.Count != 1)
- throw new NotSupportedException(SR.RangeActionsNotSupported);
+ OnRefresh();
+ else
+ OnItemMoved(args.OldItems[0], args.OldStartingIndex, args.NewStartingIndex);
}
- OnItemMoved(args.OldItems[0], args.OldStartingIndex, args.NewStartingIndex);
+ else
+ OnItemMoved(args.OldItems[0], args.OldStartingIndex, args.NewStartingIndex);
break;
case NotifyCollectionChangedAction.Reset:
diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/Primitives/Selector.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/Primitives/Selector.cs
index 6b480a12b71..181ec869992 100644
--- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/Primitives/Selector.cs
+++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/Primitives/Selector.cs
@@ -862,11 +862,16 @@ private void OnSelectedItemsCollectionChanged(object sender, NotifyCollectionCha
throw new InvalidOperationException(SR.ChangingCollectionNotSupported);
}
+ // If we have a range operation, treat it as a refresh for now
+ NotifyCollectionChangedAction effectiveAction = IsCollectionChangedRangeAction(e)
+ ? NotifyCollectionChangedAction.Reset
+ : e.Action;
+
SelectionChange.Begin();
bool succeeded=false;
try
{
- switch (e.Action)
+ switch (effectiveAction)
{
case NotifyCollectionChangedAction.Add:
if (e.NewItems.Count != 1)
@@ -924,6 +929,15 @@ private void OnSelectedItemsCollectionChanged(object sender, NotifyCollectionCha
}
}
+ private bool IsCollectionChangedRangeAction(NotifyCollectionChangedEventArgs e)
+ {
+ return
+ (e.Action == NotifyCollectionChangedAction.Add && e.NewItems.Count > 1) ||
+ (e.Action == NotifyCollectionChangedAction.Remove && e.OldItems.Count > 1) ||
+ (e.Action == NotifyCollectionChangedAction.Replace && (e.OldItems.Count > 1 || e.NewItems.Count > 1)) ||
+ (e.Action == NotifyCollectionChangedAction.Move && (e.OldItems.Count > 1 || e.NewItems.Count > 1));
+ }
+
#endregion
diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Data/BindingListCollectionView.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Data/BindingListCollectionView.cs
index f69b43febe5..fb90ac69291 100644
--- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Data/BindingListCollectionView.cs
+++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Data/BindingListCollectionView.cs
@@ -1493,7 +1493,12 @@ protected override void ProcessCollectionChanged(NotifyCollectionChangedEventArg
bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst;
bool moveCurrency = false;
- switch (args.Action)
+ // If we have a range operation, treat it as a refresh for now
+ NotifyCollectionChangedAction effectiveAction = IsCollectionChangedRangeAction(args)
+ ? NotifyCollectionChangedAction.Reset
+ : args.Action;
+
+ switch (effectiveAction)
{
case NotifyCollectionChangedAction.Add:
if (_newItemIndex == -2)
@@ -1806,6 +1811,12 @@ private IEnumerator InternalGetEnumerator()
// Data Collection immediately after the CollectionChangeEvent
private void AdjustShadowCopy(NotifyCollectionChangedEventArgs e)
{
+ // No change needed for range operations, as we just reset the collection
+ if (IsCollectionChangedRangeAction(e))
+ {
+ return;
+ }
+
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
@@ -2430,23 +2441,23 @@ private void ValidateCollectionChangedEventArgs(NotifyCollectionChangedEventArgs
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
- if (e.NewItems.Count != 1)
- throw new NotSupportedException(SR.RangeActionsNotSupported);
+ if (e.NewItems.Count < 1)
+ throw new NotSupportedException(SR.UnexpectedCollectionChangeAction);
break;
case NotifyCollectionChangedAction.Remove:
- if (e.OldItems.Count != 1)
- throw new NotSupportedException(SR.RangeActionsNotSupported);
+ if (e.OldItems.Count < 1)
+ throw new NotSupportedException(SR.UnexpectedCollectionChangeAction);
break;
case NotifyCollectionChangedAction.Replace:
- if (e.NewItems.Count != 1 || e.OldItems.Count != 1)
- throw new NotSupportedException(SR.RangeActionsNotSupported);
+ if (e.NewItems.Count < 1 || e.OldItems.Count < 1)
+ throw new NotSupportedException(SR.UnexpectedCollectionChangeAction);
break;
case NotifyCollectionChangedAction.Move:
- if (e.NewItems.Count != 1)
- throw new NotSupportedException(SR.RangeActionsNotSupported);
+ if (e.NewItems.Count < 1)
+ throw new NotSupportedException(SR.UnexpectedCollectionChangeAction);
if (e.NewStartingIndex < 0)
throw new InvalidOperationException(SR.CannotMoveToUnknownPosition);
break;
@@ -2459,6 +2470,15 @@ private void ValidateCollectionChangedEventArgs(NotifyCollectionChangedEventArgs
}
}
+ private bool IsCollectionChangedRangeAction(NotifyCollectionChangedEventArgs e)
+ {
+ return
+ (e.Action == NotifyCollectionChangedAction.Add && e.NewItems.Count > 1) ||
+ (e.Action == NotifyCollectionChangedAction.Remove && e.OldItems.Count > 1) ||
+ (e.Action == NotifyCollectionChangedAction.Replace && (e.OldItems.Count > 1 || e.NewItems.Count > 1)) ||
+ (e.Action == NotifyCollectionChangedAction.Move && (e.OldItems.Count > 1 || e.NewItems.Count > 1));
+ }
+
///
/// Helper to raise a PropertyChanged event />).
///
diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Data/CollectionView.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Data/CollectionView.cs
index eb1690348bf..2a0ffae42f3 100644
--- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Data/CollectionView.cs
+++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Data/CollectionView.cs
@@ -1075,7 +1075,12 @@ protected virtual void ProcessCollectionChanged(NotifyCollectionChangedEventArgs
int oldCurrentPosition = _currentPosition;
bool raiseChanged = false;
- switch (args.Action)
+ // If we have a range operation, treat it as a refresh for now
+ NotifyCollectionChangedAction effectiveAction = IsCollectionChangedRangeAction(args)
+ ? NotifyCollectionChangedAction.Reset
+ : args.Action;
+
+ switch (effectiveAction)
{
case NotifyCollectionChangedAction.Add:
if (PassesFilter(args.NewItems[0]))
@@ -1948,25 +1953,25 @@ private void ValidateCollectionChangedEventArgs(NotifyCollectionChangedEventArgs
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
- if (e.NewItems.Count != 1)
- throw new NotSupportedException(SR.RangeActionsNotSupported);
+ if (e.NewItems.Count < 1)
+ throw new NotSupportedException(SR.UnexpectedCollectionChangeAction);
break;
case NotifyCollectionChangedAction.Remove:
- if (e.OldItems.Count != 1)
- throw new NotSupportedException(SR.RangeActionsNotSupported);
+ if (e.OldItems.Count < 1)
+ throw new NotSupportedException(SR.UnexpectedCollectionChangeAction);
if (e.OldStartingIndex < 0)
throw new InvalidOperationException(SR.RemovedItemNotFound);
break;
case NotifyCollectionChangedAction.Replace:
- if (e.NewItems.Count != 1 || e.OldItems.Count != 1)
- throw new NotSupportedException(SR.RangeActionsNotSupported);
+ if (e.NewItems.Count < 1 || e.OldItems.Count < 1)
+ throw new NotSupportedException(SR.UnexpectedCollectionChangeAction);
break;
case NotifyCollectionChangedAction.Move:
- if (e.NewItems.Count != 1)
- throw new NotSupportedException(SR.RangeActionsNotSupported);
+ if (e.NewItems.Count < 1)
+ throw new NotSupportedException(SR.UnexpectedCollectionChangeAction);
if (e.NewStartingIndex < 0)
throw new InvalidOperationException(SR.CannotMoveToUnknownPosition);
break;
@@ -1979,6 +1984,15 @@ private void ValidateCollectionChangedEventArgs(NotifyCollectionChangedEventArgs
}
}
+ private bool IsCollectionChangedRangeAction(NotifyCollectionChangedEventArgs e)
+ {
+ return
+ (e.Action == NotifyCollectionChangedAction.Add && e.NewItems.Count > 1) ||
+ (e.Action == NotifyCollectionChangedAction.Remove && e.OldItems.Count > 1) ||
+ (e.Action == NotifyCollectionChangedAction.Replace && (e.OldItems.Count > 1 || e.NewItems.Count > 1)) ||
+ (e.Action == NotifyCollectionChangedAction.Move && (e.OldItems.Count > 1 || e.NewItems.Count > 1));
+ }
+
// fix up CurrentPosition and CurrentItem after a collection change
private void AdjustCurrencyForAdd(int index)
{
diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Data/ListCollectionView.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Data/ListCollectionView.cs
index ce8a01d0b8e..ab8a075d6f2 100644
--- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Data/ListCollectionView.cs
+++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Data/ListCollectionView.cs
@@ -1657,6 +1657,8 @@ protected override void ProcessCollectionChanged(NotifyCollectionChangedEventArg
ValidateCollectionChangedEventArgs(args);
+ bool isRangeAction = IsCollectionChangedRangeAction(args);
+
// adding or replacing an item can change CanAddNew, by providing a
// non-null representative
if (!_isItemConstructorValid)
@@ -1677,7 +1679,8 @@ protected override void ProcessCollectionChanged(NotifyCollectionChangedEventArg
// apply the change to the shadow copy
if (AllowsCrossThreadChanges)
{
- if (args.Action != NotifyCollectionChangedAction.Reset)
+ // ignore when we have a range action, as we'll just reset the collection
+ if (args.Action != NotifyCollectionChangedAction.Reset && !isRangeAction)
{
if (args.Action != NotifyCollectionChangedAction.Remove && args.NewStartingIndex < 0
|| args.Action != NotifyCollectionChangedAction.Add && args.OldStartingIndex < 0)
@@ -1693,7 +1696,8 @@ protected override void ProcessCollectionChanged(NotifyCollectionChangedEventArg
}
// If the Action is Reset then we do a Refresh.
- if (args.Action == NotifyCollectionChangedAction.Reset)
+ // Also treat range actions as Reset, for now.
+ if (args.Action == NotifyCollectionChangedAction.Reset || isRangeAction)
{
// implicitly cancel EditItem transactions
if (IsEditingItem)
@@ -2497,23 +2501,23 @@ private void ValidateCollectionChangedEventArgs(NotifyCollectionChangedEventArgs
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
- if (e.NewItems.Count != 1)
- throw new NotSupportedException(SR.RangeActionsNotSupported);
+ if (e.NewItems.Count < 1)
+ throw new NotSupportedException(SR.UnexpectedCollectionChangeAction);
break;
case NotifyCollectionChangedAction.Remove:
- if (e.OldItems.Count != 1)
- throw new NotSupportedException(SR.RangeActionsNotSupported);
+ if (e.OldItems.Count < 1)
+ throw new NotSupportedException(SR.UnexpectedCollectionChangeAction);
break;
case NotifyCollectionChangedAction.Replace:
- if (e.NewItems.Count != 1 || e.OldItems.Count != 1)
- throw new NotSupportedException(SR.RangeActionsNotSupported);
+ if (e.NewItems.Count < 1 || e.OldItems.Count < 1)
+ throw new NotSupportedException(SR.UnexpectedCollectionChangeAction);
break;
case NotifyCollectionChangedAction.Move:
- if (e.NewItems.Count != 1)
- throw new NotSupportedException(SR.RangeActionsNotSupported);
+ if (e.NewItems.Count < 1)
+ throw new NotSupportedException(SR.UnexpectedCollectionChangeAction);
if (e.NewStartingIndex < 0)
throw new InvalidOperationException(SR.CannotMoveToUnknownPosition);
break;
@@ -2526,6 +2530,15 @@ private void ValidateCollectionChangedEventArgs(NotifyCollectionChangedEventArgs
}
}
+ private bool IsCollectionChangedRangeAction(NotifyCollectionChangedEventArgs e)
+ {
+ return
+ (e.Action == NotifyCollectionChangedAction.Add && e.NewItems.Count > 1) ||
+ (e.Action == NotifyCollectionChangedAction.Remove && e.OldItems.Count > 1) ||
+ (e.Action == NotifyCollectionChangedAction.Replace && (e.OldItems.Count > 1 || e.NewItems.Count > 1)) ||
+ (e.Action == NotifyCollectionChangedAction.Move && (e.OldItems.Count > 1 || e.NewItems.Count > 1));
+ }
+
///
/// Create, filter and sort the local index array.