Skip to content

Commit 461531a

Browse files
committed
Minimal support for observable range actions
1 parent ac5efa2 commit 461531a

File tree

6 files changed

+137
-49
lines changed

6 files changed

+137
-49
lines changed

src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/Data/CompositeCollectionView.cs

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -384,9 +384,16 @@ protected override void ProcessCollectionChanged(NotifyCollectionChangedEventArg
384384
{
385385
ValidateCollectionChangedEventArgs(args);
386386

387+
bool isRangeAction = IsCollectionChangedRangeAction(args);
388+
387389
bool moveCurrencyOffDeletedElement = false;
388390

389-
switch (args.Action)
391+
// If we have a range operation, treat it as a refresh for now
392+
NotifyCollectionChangedAction effectiveAction = isRangeAction
393+
? NotifyCollectionChangedAction.Reset
394+
: args.Action;
395+
396+
switch (effectiveAction)
390397
{
391398
case NotifyCollectionChangedAction.Add:
392399
case NotifyCollectionChangedAction.Remove:
@@ -608,7 +615,7 @@ protected override void ProcessCollectionChanged(NotifyCollectionChangedEventArg
608615

609616
case NotifyCollectionChangedAction.Reset:
610617
{
611-
_traceLog?.Add("ProcessCollectionChanged action = {0}", args.Action);
618+
_traceLog?.Add("ProcessCollectionChanged action = {0}", effectiveAction);
612619

613620
if (_collection.Count != 0)
614621
{
@@ -1430,23 +1437,23 @@ private void ValidateCollectionChangedEventArgs(NotifyCollectionChangedEventArgs
14301437
switch (e.Action)
14311438
{
14321439
case NotifyCollectionChangedAction.Add:
1433-
if (e.NewItems.Count != 1)
1434-
throw new NotSupportedException(SR.RangeActionsNotSupported);
1440+
if (e.NewItems.Count < 1)
1441+
throw new NotSupportedException(SR.UnexpectedCollectionChangeAction);
14351442
break;
14361443

14371444
case NotifyCollectionChangedAction.Remove:
1438-
if (e.OldItems.Count != 1)
1439-
throw new NotSupportedException(SR.RangeActionsNotSupported);
1445+
if (e.OldItems.Count < 1)
1446+
throw new NotSupportedException(SR.UnexpectedCollectionChangeAction);
14401447
break;
14411448

14421449
case NotifyCollectionChangedAction.Replace:
1443-
if (e.NewItems.Count != 1 || e.OldItems.Count != 1)
1444-
throw new NotSupportedException(SR.RangeActionsNotSupported);
1450+
if (e.NewItems.Count < 1 || e.OldItems.Count < 1)
1451+
throw new NotSupportedException(SR.UnexpectedCollectionChangeAction);
14451452
break;
14461453

14471454
case NotifyCollectionChangedAction.Move:
1448-
if (e.NewItems.Count != 1)
1449-
throw new NotSupportedException(SR.RangeActionsNotSupported);
1455+
if (e.NewItems.Count < 1)
1456+
throw new NotSupportedException(SR.UnexpectedCollectionChangeAction);
14501457
if (e.NewStartingIndex < 0)
14511458
throw new InvalidOperationException(SR.CannotMoveToUnknownPosition);
14521459
break;
@@ -1459,6 +1466,15 @@ private void ValidateCollectionChangedEventArgs(NotifyCollectionChangedEventArgs
14591466
}
14601467
}
14611468

1469+
private bool IsCollectionChangedRangeAction(NotifyCollectionChangedEventArgs e)
1470+
{
1471+
return
1472+
(e.Action == NotifyCollectionChangedAction.Add && e.NewItems.Count > 1) ||
1473+
(e.Action == NotifyCollectionChangedAction.Remove && e.OldItems.Count > 1) ||
1474+
(e.Action == NotifyCollectionChangedAction.Replace && (e.OldItems.Count > 1 || e.NewItems.Count > 1)) ||
1475+
(e.Action == NotifyCollectionChangedAction.Move && (e.OldItems.Count > 1 || e.NewItems.Count > 1));
1476+
}
1477+
14621478
/// <summary>
14631479
/// Helper to raise a PropertyChanged event />).
14641480
/// </summary>

src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/ItemContainerGenerator.cs

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2388,35 +2388,46 @@ private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs
23882388
switch (args.Action)
23892389
{
23902390
case NotifyCollectionChangedAction.Add:
2391+
2392+
// Treat range operations as refresh operations, for minimum support.
2393+
// This can be expanded later with a better implementation, if needed.
23912394
if (args.NewItems.Count != 1)
2392-
throw new NotSupportedException(SR.RangeActionsNotSupported);
2393-
OnItemAdded(args.NewItems[0], args.NewStartingIndex);
2395+
OnRefresh();
2396+
else
2397+
OnItemAdded(args.NewItems[0], args.NewStartingIndex);
23942398
break;
23952399

23962400
case NotifyCollectionChangedAction.Remove:
23972401
if (args.OldItems.Count != 1)
2398-
throw new NotSupportedException(SR.RangeActionsNotSupported);
2399-
OnItemRemoved(args.OldItems[0], args.OldStartingIndex);
2402+
OnRefresh();
2403+
else
2404+
OnItemRemoved(args.OldItems[0], args.OldStartingIndex);
24002405
break;
24012406

24022407
case NotifyCollectionChangedAction.Replace:
2403-
// Don't check arguments if app targets 4.0, for compat ( 726682)
2408+
// Don't check arguments if app targets 4.0, for compat (726682)
24042409
if (!FrameworkCompatibilityPreferences.TargetsDesktop_V4_0)
24052410
{
24062411
if (args.OldItems.Count != 1)
2407-
throw new NotSupportedException(SR.RangeActionsNotSupported);
2412+
OnRefresh();
2413+
else
2414+
OnItemReplaced(args.OldItems[0], args.NewItems[0], args.NewStartingIndex);
24082415
}
2409-
OnItemReplaced(args.OldItems[0], args.NewItems[0], args.NewStartingIndex);
2416+
else
2417+
OnItemReplaced(args.OldItems[0], args.NewItems[0], args.NewStartingIndex);
24102418
break;
24112419

24122420
case NotifyCollectionChangedAction.Move:
2413-
// Don't check arguments if app targets 4.0, for compat ( 726682)
2421+
// Don't check arguments if app targets 4.0, for compat (726682)
24142422
if (!FrameworkCompatibilityPreferences.TargetsDesktop_V4_0)
24152423
{
24162424
if (args.OldItems.Count != 1)
2417-
throw new NotSupportedException(SR.RangeActionsNotSupported);
2425+
OnRefresh();
2426+
else
2427+
OnItemMoved(args.OldItems[0], args.OldStartingIndex, args.NewStartingIndex);
24182428
}
2419-
OnItemMoved(args.OldItems[0], args.OldStartingIndex, args.NewStartingIndex);
2429+
else
2430+
OnItemMoved(args.OldItems[0], args.OldStartingIndex, args.NewStartingIndex);
24202431
break;
24212432

24222433
case NotifyCollectionChangedAction.Reset:

src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/Primitives/Selector.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -862,11 +862,16 @@ private void OnSelectedItemsCollectionChanged(object sender, NotifyCollectionCha
862862
throw new InvalidOperationException(SR.ChangingCollectionNotSupported);
863863
}
864864

865+
// If we have a range operation, treat it as a refresh for now
866+
NotifyCollectionChangedAction effectiveAction = IsCollectionChangedRangeAction(e)
867+
? NotifyCollectionChangedAction.Reset
868+
: args.Action;
869+
865870
SelectionChange.Begin();
866871
bool succeeded=false;
867872
try
868873
{
869-
switch (e.Action)
874+
switch (effectiveAction)
870875
{
871876
case NotifyCollectionChangedAction.Add:
872877
if (e.NewItems.Count != 1)
@@ -924,6 +929,15 @@ private void OnSelectedItemsCollectionChanged(object sender, NotifyCollectionCha
924929
}
925930
}
926931

932+
private bool IsCollectionChangedRangeAction(NotifyCollectionChangedEventArgs e)
933+
{
934+
return
935+
(e.Action == NotifyCollectionChangedAction.Add && e.NewItems.Count > 1) ||
936+
(e.Action == NotifyCollectionChangedAction.Remove && e.OldItems.Count > 1) ||
937+
(e.Action == NotifyCollectionChangedAction.Replace && (e.OldItems.Count > 1 || e.NewItems.Count > 1)) ||
938+
(e.Action == NotifyCollectionChangedAction.Move && (e.OldItems.Count > 1 || e.NewItems.Count > 1));
939+
}
940+
927941
#endregion
928942

929943

src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Data/BindingListCollectionView.cs

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1493,7 +1493,12 @@ protected override void ProcessCollectionChanged(NotifyCollectionChangedEventArg
14931493
bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst;
14941494
bool moveCurrency = false;
14951495

1496-
switch (args.Action)
1496+
// If we have a range operation, treat it as a refresh for now
1497+
NotifyCollectionChangedAction effectiveAction = IsCollectionChangedRangeAction(args)
1498+
? NotifyCollectionChangedAction.Reset
1499+
: args.Action;
1500+
1501+
switch (effectiveAction)
14971502
{
14981503
case NotifyCollectionChangedAction.Add:
14991504
if (_newItemIndex == -2)
@@ -1806,6 +1811,12 @@ private IEnumerator InternalGetEnumerator()
18061811
// Data Collection immediately after the CollectionChangeEvent
18071812
private void AdjustShadowCopy(NotifyCollectionChangedEventArgs e)
18081813
{
1814+
// No change needed for range operations, as we just reset the collection
1815+
if (IsCollectionChangedRangeAction(e))
1816+
{
1817+
return;
1818+
}
1819+
18091820
switch (e.Action)
18101821
{
18111822
case NotifyCollectionChangedAction.Add:
@@ -2430,23 +2441,23 @@ private void ValidateCollectionChangedEventArgs(NotifyCollectionChangedEventArgs
24302441
switch (e.Action)
24312442
{
24322443
case NotifyCollectionChangedAction.Add:
2433-
if (e.NewItems.Count != 1)
2434-
throw new NotSupportedException(SR.RangeActionsNotSupported);
2444+
if (e.NewItems.Count < 1)
2445+
throw new NotSupportedException(SR.UnexpectedCollectionChangeAction);
24352446
break;
24362447

24372448
case NotifyCollectionChangedAction.Remove:
2438-
if (e.OldItems.Count != 1)
2439-
throw new NotSupportedException(SR.RangeActionsNotSupported);
2449+
if (e.OldItems.Count < 1)
2450+
throw new NotSupportedException(SR.UnexpectedCollectionChangeAction);
24402451
break;
24412452

24422453
case NotifyCollectionChangedAction.Replace:
2443-
if (e.NewItems.Count != 1 || e.OldItems.Count != 1)
2444-
throw new NotSupportedException(SR.RangeActionsNotSupported);
2454+
if (e.NewItems.Count < 1 || e.OldItems.Count < 1)
2455+
throw new NotSupportedException(SR.UnexpectedCollectionChangeAction);
24452456
break;
24462457

24472458
case NotifyCollectionChangedAction.Move:
2448-
if (e.NewItems.Count != 1)
2449-
throw new NotSupportedException(SR.RangeActionsNotSupported);
2459+
if (e.NewItems.Count < 1)
2460+
throw new NotSupportedException(SR.UnexpectedCollectionChangeAction);
24502461
if (e.NewStartingIndex < 0)
24512462
throw new InvalidOperationException(SR.CannotMoveToUnknownPosition);
24522463
break;
@@ -2459,6 +2470,15 @@ private void ValidateCollectionChangedEventArgs(NotifyCollectionChangedEventArgs
24592470
}
24602471
}
24612472

2473+
private bool IsCollectionChangedRangeAction(NotifyCollectionChangedEventArgs e)
2474+
{
2475+
return
2476+
(e.Action == NotifyCollectionChangedAction.Add && e.NewItems.Count > 1) ||
2477+
(e.Action == NotifyCollectionChangedAction.Remove && e.OldItems.Count > 1) ||
2478+
(e.Action == NotifyCollectionChangedAction.Replace && (e.OldItems.Count > 1 || e.NewItems.Count > 1)) ||
2479+
(e.Action == NotifyCollectionChangedAction.Move && (e.OldItems.Count > 1 || e.NewItems.Count > 1));
2480+
}
2481+
24622482
/// <summary>
24632483
/// Helper to raise a PropertyChanged event />).
24642484
/// </summary>

src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Data/CollectionView.cs

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1075,7 +1075,12 @@ protected virtual void ProcessCollectionChanged(NotifyCollectionChangedEventArgs
10751075
int oldCurrentPosition = _currentPosition;
10761076
bool raiseChanged = false;
10771077

1078-
switch (args.Action)
1078+
// If we have a range operation, treat it as a refresh for now
1079+
NotifyCollectionChangedAction effectiveAction = IsCollectionChangedRangeAction(args)
1080+
? NotifyCollectionChangedAction.Reset
1081+
: args.Action;
1082+
1083+
switch (effectiveAction)
10791084
{
10801085
case NotifyCollectionChangedAction.Add:
10811086
if (PassesFilter(args.NewItems[0]))
@@ -1948,25 +1953,25 @@ private void ValidateCollectionChangedEventArgs(NotifyCollectionChangedEventArgs
19481953
switch (e.Action)
19491954
{
19501955
case NotifyCollectionChangedAction.Add:
1951-
if (e.NewItems.Count != 1)
1952-
throw new NotSupportedException(SR.RangeActionsNotSupported);
1956+
if (e.NewItems.Count < 1)
1957+
throw new NotSupportedException(SR.UnexpectedCollectionChangeAction);
19531958
break;
19541959

19551960
case NotifyCollectionChangedAction.Remove:
1956-
if (e.OldItems.Count != 1)
1957-
throw new NotSupportedException(SR.RangeActionsNotSupported);
1961+
if (e.OldItems.Count < 1)
1962+
throw new NotSupportedException(SR.UnexpectedCollectionChangeAction);
19581963
if (e.OldStartingIndex < 0)
19591964
throw new InvalidOperationException(SR.RemovedItemNotFound);
19601965
break;
19611966

19621967
case NotifyCollectionChangedAction.Replace:
1963-
if (e.NewItems.Count != 1 || e.OldItems.Count != 1)
1964-
throw new NotSupportedException(SR.RangeActionsNotSupported);
1968+
if (e.NewItems.Count < 1 || e.OldItems.Count < 1)
1969+
throw new NotSupportedException(SR.UnexpectedCollectionChangeAction);
19651970
break;
19661971

19671972
case NotifyCollectionChangedAction.Move:
1968-
if (e.NewItems.Count != 1)
1969-
throw new NotSupportedException(SR.RangeActionsNotSupported);
1973+
if (e.NewItems.Count < 1)
1974+
throw new NotSupportedException(SR.UnexpectedCollectionChangeAction);
19701975
if (e.NewStartingIndex < 0)
19711976
throw new InvalidOperationException(SR.CannotMoveToUnknownPosition);
19721977
break;
@@ -1979,6 +1984,15 @@ private void ValidateCollectionChangedEventArgs(NotifyCollectionChangedEventArgs
19791984
}
19801985
}
19811986

1987+
private bool IsCollectionChangedRangeAction(NotifyCollectionChangedEventArgs e)
1988+
{
1989+
return
1990+
(e.Action == NotifyCollectionChangedAction.Add && e.NewItems.Count > 1) ||
1991+
(e.Action == NotifyCollectionChangedAction.Remove && e.OldItems.Count > 1) ||
1992+
(e.Action == NotifyCollectionChangedAction.Replace && (e.OldItems.Count > 1 || e.NewItems.Count > 1)) ||
1993+
(e.Action == NotifyCollectionChangedAction.Move && (e.OldItems.Count > 1 || e.NewItems.Count > 1));
1994+
}
1995+
19821996
// fix up CurrentPosition and CurrentItem after a collection change
19831997
private void AdjustCurrencyForAdd(int index)
19841998
{

src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Data/ListCollectionView.cs

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1657,6 +1657,8 @@ protected override void ProcessCollectionChanged(NotifyCollectionChangedEventArg
16571657

16581658
ValidateCollectionChangedEventArgs(args);
16591659

1660+
bool isRangeAction = IsCollectionChangedRangeAction(args);
1661+
16601662
// adding or replacing an item can change CanAddNew, by providing a
16611663
// non-null representative
16621664
if (!_isItemConstructorValid)
@@ -1677,7 +1679,8 @@ protected override void ProcessCollectionChanged(NotifyCollectionChangedEventArg
16771679
// apply the change to the shadow copy
16781680
if (AllowsCrossThreadChanges)
16791681
{
1680-
if (args.Action != NotifyCollectionChangedAction.Reset)
1682+
// ignore when we have a range action, as we'll just reset the collection
1683+
if (args.Action != NotifyCollectionChangedAction.Reset && !isRangeAction)
16811684
{
16821685
if (args.Action != NotifyCollectionChangedAction.Remove && args.NewStartingIndex < 0
16831686
|| args.Action != NotifyCollectionChangedAction.Add && args.OldStartingIndex < 0)
@@ -1693,7 +1696,8 @@ protected override void ProcessCollectionChanged(NotifyCollectionChangedEventArg
16931696
}
16941697

16951698
// If the Action is Reset then we do a Refresh.
1696-
if (args.Action == NotifyCollectionChangedAction.Reset)
1699+
// Also treat range actions as Reset, for now.
1700+
if (args.Action == NotifyCollectionChangedAction.Reset || isRangeAction)
16971701
{
16981702
// implicitly cancel EditItem transactions
16991703
if (IsEditingItem)
@@ -2497,23 +2501,23 @@ private void ValidateCollectionChangedEventArgs(NotifyCollectionChangedEventArgs
24972501
switch (e.Action)
24982502
{
24992503
case NotifyCollectionChangedAction.Add:
2500-
if (e.NewItems.Count != 1)
2501-
throw new NotSupportedException(SR.RangeActionsNotSupported);
2504+
if (e.NewItems.Count < 1)
2505+
throw new NotSupportedException(SR.UnexpectedCollectionChangeAction);
25022506
break;
25032507

25042508
case NotifyCollectionChangedAction.Remove:
2505-
if (e.OldItems.Count != 1)
2506-
throw new NotSupportedException(SR.RangeActionsNotSupported);
2509+
if (e.OldItems.Count < 1)
2510+
throw new NotSupportedException(SR.UnexpectedCollectionChangeAction);
25072511
break;
25082512

25092513
case NotifyCollectionChangedAction.Replace:
2510-
if (e.NewItems.Count != 1 || e.OldItems.Count != 1)
2511-
throw new NotSupportedException(SR.RangeActionsNotSupported);
2514+
if (e.NewItems.Count < 1 || e.OldItems.Count < 1)
2515+
throw new NotSupportedException(SR.UnexpectedCollectionChangeAction);
25122516
break;
25132517

25142518
case NotifyCollectionChangedAction.Move:
2515-
if (e.NewItems.Count != 1)
2516-
throw new NotSupportedException(SR.RangeActionsNotSupported);
2519+
if (e.NewItems.Count < 1)
2520+
throw new NotSupportedException(SR.UnexpectedCollectionChangeAction);
25172521
if (e.NewStartingIndex < 0)
25182522
throw new InvalidOperationException(SR.CannotMoveToUnknownPosition);
25192523
break;
@@ -2526,6 +2530,15 @@ private void ValidateCollectionChangedEventArgs(NotifyCollectionChangedEventArgs
25262530
}
25272531
}
25282532

2533+
private bool IsCollectionChangedRangeAction(NotifyCollectionChangedEventArgs e)
2534+
{
2535+
return
2536+
(e.Action == NotifyCollectionChangedAction.Add && e.NewItems.Count > 1) ||
2537+
(e.Action == NotifyCollectionChangedAction.Remove && e.OldItems.Count > 1) ||
2538+
(e.Action == NotifyCollectionChangedAction.Replace && (e.OldItems.Count > 1 || e.NewItems.Count > 1)) ||
2539+
(e.Action == NotifyCollectionChangedAction.Move && (e.OldItems.Count > 1 || e.NewItems.Count > 1));
2540+
}
2541+
25292542

25302543
/// <summary>
25312544
/// Create, filter and sort the local index array.

0 commit comments

Comments
 (0)