Skip to content
This repository was archived by the owner on Feb 21, 2023. It is now read-only.

Commit 94de370

Browse files
Merge pull request Catel#1064 from Catel/feature/853
(*) Catel#853 Improve Mixed mode in FastObservableCollection
2 parents a09d1ad + b6cfe4e commit 94de370

8 files changed

Lines changed: 399 additions & 116 deletions

File tree

doc/history.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ Added/fixed:
9292
(*) #135 Use NuGet dependency for System.Windows.Interactivity instead
9393
(*) #807 Exceptions in TaskCommand are now longer swallowed by default. Pre 5.0 behavior can be mimmicked by setting
9494
the TaskCommand.SwallowExceptions property to true
95+
(*) #853 Improve Mixed mode in FastObservableCollection and FastBindingList
9596
(*) #901 Clean up FrameworkElement and DependencyObject extension methods
9697
(*) #988 Set default value of DeferValidationUntilFirstSaveCall to true
9798
(*) #995 Split ModelBase into ModelBase (without validation) and ValidatableModelBase

src/Catel.MVVM/Catel.MVVM.Shared/Collections/EventArgs/NotifyRangedListChangedEventArgs.cs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,19 @@ public NotifyRangedListChangedEventArgs(NotifyRangedListChangedAction action, IL
4141
{
4242
Action = action;
4343

44-
NewItems = changedItems;
45-
NewStartingIndex = (indices != null && indices.Count != 0) ? indices[0] : -1;
46-
OldStartingIndex = -1;
44+
var startingIndex = indices != null && indices.Count != 0 ? indices[0] : -1;
45+
if (action == NotifyRangedListChangedAction.Add)
46+
{
47+
NewItems = changedItems;
48+
NewStartingIndex = startingIndex;
49+
OldStartingIndex = -1;
50+
}
51+
else
52+
{
53+
OldItems = changedItems;
54+
OldStartingIndex = startingIndex;
55+
NewStartingIndex = -1;
56+
}
4757

4858
Indices = indices;
4959
}

src/Catel.MVVM/Catel.MVVM.Shared/Collections/FastBindingList.cs

Lines changed: 41 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,7 @@ namespace Catel.Collections
1111
using System;
1212
using System.Collections;
1313
using System.Collections.Generic;
14-
using System.Collections.ObjectModel;
15-
using System.Collections.Specialized;
1614
using System.ComponentModel;
17-
using System.Diagnostics;
18-
1915
using Catel.Logging;
2016

2117
using IoC;
@@ -242,7 +238,7 @@ public void AddItems(IEnumerable<T> collection)
242238
{
243239
Argument.IsNotNull("collection", collection);
244240

245-
using (SuspendChangeNotifications(SuspensionMode.Adding))
241+
using (SuspendChangeNotifications(_suspensionContext?.Mode ?? SuspensionMode.Adding))
246242
{
247243
foreach (var item in collection)
248244
{
@@ -264,7 +260,7 @@ public void AddItems(IEnumerable collection)
264260

265261
var list = (IList)this;
266262

267-
using (SuspendChangeNotifications(SuspensionMode.Adding))
263+
using (SuspendChangeNotifications(_suspensionContext?.Mode ?? SuspensionMode.Adding))
268264
{
269265
foreach (var item in collection)
270266
{
@@ -284,7 +280,7 @@ public void RemoveItems(IEnumerable<T> collection)
284280
{
285281
Argument.IsNotNull("collection", collection);
286282

287-
using (SuspendChangeNotifications(SuspensionMode.Removing))
283+
using (SuspendChangeNotifications(_suspensionContext?.Mode ?? SuspensionMode.Removing))
288284
{
289285
foreach (var item in collection)
290286
{
@@ -306,7 +302,7 @@ public void RemoveItems(IEnumerable collection)
306302

307303
var list = (IList)this;
308304

309-
using (SuspendChangeNotifications(SuspensionMode.Removing))
305+
using (SuspendChangeNotifications(_suspensionContext?.Mode ?? SuspensionMode.Removing))
310306
{
311307
foreach (var item in collection)
312308
{
@@ -410,28 +406,28 @@ protected void NotifyChanges()
410406
Action action = () =>
411407
{
412408
// Create event args
413-
ListChangedEventArgs eventArgs = null;
414-
if (_suspensionContext != null && _suspensionContext.Mode == SuspensionMode.Adding)
409+
List<ListChangedEventArgs> eventArgsList = new List<ListChangedEventArgs>();
410+
411+
var suspensionContext = _suspensionContext;
412+
if (suspensionContext != null)
415413
{
416-
if (_suspensionContext.NewItems.Count != 0)
414+
if (suspensionContext.NewItems.Count != 0)
417415
{
418-
eventArgs = new NotifyRangedListChangedEventArgs(NotifyRangedListChangedAction.Add, _suspensionContext.NewItems, _suspensionContext.NewItemIndices);
416+
eventArgsList.Add(new NotifyRangedListChangedEventArgs(NotifyRangedListChangedAction.Add, suspensionContext.NewItems, suspensionContext.NewItemIndices));
419417
}
420-
}
421-
else if (_suspensionContext != null && _suspensionContext.Mode == SuspensionMode.Removing)
422-
{
423-
if (_suspensionContext.OldItems.Count != 0)
418+
419+
if (suspensionContext.OldItems.Count != 0)
424420
{
425-
eventArgs = new NotifyRangedListChangedEventArgs(NotifyRangedListChangedAction.Remove, _suspensionContext.OldItems, _suspensionContext.OldItemIndices);
421+
eventArgsList.Add(new NotifyRangedListChangedEventArgs(NotifyRangedListChangedAction.Remove, suspensionContext.OldItems, suspensionContext.OldItemIndices));
426422
}
427423
}
428424
else
429425
{
430-
eventArgs = new NotifyListChangedEventArgs(ListChangedType.Reset);
426+
eventArgsList.Add(new NotifyListChangedEventArgs(ListChangedType.Reset));
431427
}
432428

433429
// Fire events
434-
if (eventArgs != null)
430+
foreach (var eventArgs in eventArgsList)
435431
{
436432
OnListChanged(eventArgs);
437433
}
@@ -552,8 +548,18 @@ protected override void ClearItems()
552548
throw Log.ErrorAndCreateException<InvalidOperationException>($"Clearing items is only allowed in SuspensionMode.None, current mode is '{_suspensionContext.Mode}'.");
553549
}
554550

555-
// Call base
556-
base.ClearItems();
551+
if (_suspensionContext != null && _suspensionContext.Mode == SuspensionMode.None)
552+
{
553+
while (Count > 0)
554+
{
555+
RemoveItem(0);
556+
}
557+
}
558+
else
559+
{
560+
// Call base
561+
base.ClearItems();
562+
}
557563
}
558564

559565
/// <summary>
@@ -595,19 +601,24 @@ protected override int FindCore(PropertyDescriptor prop, object key)
595601
protected override void InsertItem(int index, T item)
596602
{
597603
// Check
598-
if (_suspensionContext != null && _suspensionContext.Mode == SuspensionMode.Removing)
604+
var suspensionContext = _suspensionContext;
605+
if (suspensionContext != null && suspensionContext.Mode == SuspensionMode.Removing)
599606
{
600607
throw Log.ErrorAndCreateException<InvalidOperationException>("Adding items is not allowed in mode SuspensionMode.Removing.");
601608
}
602609

603610
// Call base
604611
var oldValue = RaiseListChangedEvents;
605612
RaiseListChangedEvents = false;
613+
614+
bool? removed;
606615
try
607616
{
617+
removed = suspensionContext?.TryRemoveItemFromOldItems(index, item);
618+
608619
base.InsertItem(index, item);
609620

610-
if (!NotificationsSuspended)
621+
if (suspensionContext == null)
611622
{
612623
OnListChanged(new NotifyListChangedEventArgs(ListChangedType.ItemAdded, index, item));
613624
}
@@ -617,11 +628,11 @@ protected override void InsertItem(int index, T item)
617628
RaiseListChangedEvents = oldValue;
618629
}
619630

620-
if (_suspensionContext != null && _suspensionContext.Mode == SuspensionMode.Adding)
631+
if (removed != null && !removed.Value)
621632
{
622633
// Remember
623-
_suspensionContext.NewItems.Add(item);
624-
_suspensionContext.NewItemIndices.Add(index);
634+
suspensionContext.NewItems.Add(item);
635+
suspensionContext.NewItemIndices.Add(index);
625636
}
626637
}
627638

@@ -643,8 +654,11 @@ protected override void RemoveItem(int index)
643654
// Call base
644655
var oldValue = RaiseListChangedEvents;
645656
RaiseListChangedEvents = false;
657+
658+
bool? removed;
646659
try
647660
{
661+
removed = _suspensionContext?.TryRemoveItemFromNewItems(index, item);
648662
base.RemoveItem(index);
649663

650664
if (!NotificationsSuspended)
@@ -657,7 +671,7 @@ protected override void RemoveItem(int index)
657671
RaiseListChangedEvents = oldValue;
658672
}
659673

660-
if (_suspensionContext != null && _suspensionContext.Mode == SuspensionMode.Removing)
674+
if (removed != null && !removed.Value)
661675
{
662676
// Remember
663677
_suspensionContext.OldItems.Add(item);

src/Catel.MVVM/Catel.MVVM.Shared/Collections/FastObservableCollection.cs

Lines changed: 52 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ namespace Catel.Collections
1212
using System.Collections.ObjectModel;
1313
using System.Collections.Specialized;
1414
using System.ComponentModel;
15-
using System.Diagnostics;
16-
1715
using Catel.Logging;
1816

1917
using IoC;
@@ -47,6 +45,9 @@ public class FastObservableCollection<T> : ObservableCollection<T>, ISuspendChan
4745
[field: NonSerialized]
4846
#endif
4947
private SuspensionContext<T> _suspensionContext;
48+
49+
private readonly Stack<CollectionChangeEventArgs> _collectionEventArgs = new Stack<CollectionChangeEventArgs>();
50+
5051
#endregion
5152

5253
#region Constructors
@@ -427,36 +428,33 @@ protected void NotifyChanges()
427428
{
428429
Action action = () =>
429430
{
430-
// Create event args
431-
NotifyCollectionChangedEventArgs eventArgs = null;
432-
if (_suspensionContext != null && _suspensionContext.Mode == SuspensionMode.Adding)
431+
List<NotifyCollectionChangedEventArgs> eventArgsList = new List<NotifyCollectionChangedEventArgs>();
432+
433+
var suspensionContext = _suspensionContext;
434+
if (suspensionContext != null)
433435
{
434-
if (_suspensionContext.NewItems.Count != 0)
436+
if (suspensionContext.NewItems.Count != 0)
435437
{
436-
eventArgs = CreateEventArgs(NotifyCollectionChangedAction.Add, _suspensionContext.NewItems, _suspensionContext.NewItemIndices);
438+
eventArgsList.Add(CreateEventArgs(NotifyCollectionChangedAction.Add, suspensionContext.NewItems, suspensionContext.NewItemIndices));
437439
}
438-
}
439-
else if (_suspensionContext != null && _suspensionContext.Mode == SuspensionMode.Removing)
440-
{
441-
if (_suspensionContext.OldItems.Count != 0)
440+
441+
if (suspensionContext.OldItems.Count != 0)
442442
{
443-
eventArgs = CreateEventArgs(NotifyCollectionChangedAction.Remove, _suspensionContext.OldItems, _suspensionContext.OldItemIndices);
443+
eventArgsList.Add(CreateEventArgs(NotifyCollectionChangedAction.Remove, suspensionContext.OldItems, suspensionContext.OldItemIndices));
444444
}
445445
}
446446
else
447447
{
448-
//Debug.Assert(_suspensionContext != null && _suspensionContext.Mode == SuspensionMode.None, "Wrong/unknown suspension mode!");
449-
450-
eventArgs = CreateEventArgs(NotifyCollectionChangedAction.Reset);
448+
eventArgsList.Add(CreateEventArgs(NotifyCollectionChangedAction.Reset));
451449
}
452450

453-
// Fire events
454-
if (eventArgs != null)
451+
foreach (var eventArgs in eventArgsList)
455452
{
456-
OnPropertyChanged(new PropertyChangedEventArgs("Count"));
457-
OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
458453
OnCollectionChanged(eventArgs);
459454
}
455+
456+
OnPropertyChanged(new PropertyChangedEventArgs("Count"));
457+
OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
460458
};
461459

462460
if (AutomaticallyDispatchChangeNotifications)
@@ -475,7 +473,8 @@ protected void NotifyChanges()
475473
/// <param name="e">The <see cref="NotifyCollectionChangedEventArgs" /> instance containing the event data.</param>
476474
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
477475
{
478-
if (_suspensionContext == null || (_suspensionContext != null && _suspensionContext.Count == 0))
476+
var suspensionContext = _suspensionContext;
477+
if (suspensionContext == null || suspensionContext.Count == 0)
479478
{
480479
if (AutomaticallyDispatchChangeNotifications)
481480
{
@@ -489,7 +488,7 @@ protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
489488
return;
490489
}
491490

492-
if (_suspensionContext != null && _suspensionContext.Count != 0)
491+
if (suspensionContext.Count != 0)
493492
{
494493
IsDirty = true;
495494
}
@@ -501,7 +500,8 @@ protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
501500
/// <param name="e">The <see cref="PropertyChangedEventArgs" /> instance containing the event data.</param>
502501
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
503502
{
504-
if (_suspensionContext == null || (_suspensionContext != null && _suspensionContext.Count == 0))
503+
var suspensionContext = _suspensionContext;
504+
if (suspensionContext == null || suspensionContext.Count == 0)
505505
{
506506
if (AutomaticallyDispatchChangeNotifications)
507507
{
@@ -521,13 +521,24 @@ protected override void OnPropertyChanged(PropertyChangedEventArgs e)
521521
protected override void ClearItems()
522522
{
523523
// Check
524-
if (_suspensionContext != null && _suspensionContext.Mode != SuspensionMode.None)
524+
var suspensionContext = _suspensionContext;
525+
if (suspensionContext != null && suspensionContext.Mode != SuspensionMode.None)
525526
{
526-
throw Log.ErrorAndCreateException<InvalidOperationException>($"Clearing items is only allowed in SuspensionMode.None, current mode is '{_suspensionContext.Mode}'");
527+
throw Log.ErrorAndCreateException<InvalidOperationException>($"Clearing items is only allowed in SuspensionMode.None, current mode is '{suspensionContext.Mode}'");
527528
}
528529

529-
// Call base
530-
base.ClearItems();
530+
if (_suspensionContext != null && _suspensionContext.Mode == SuspensionMode.None)
531+
{
532+
while (Count > 0)
533+
{
534+
RemoveItem(0);
535+
}
536+
}
537+
else
538+
{
539+
// Call base
540+
base.ClearItems();
541+
}
531542
}
532543

533544
/// <summary>
@@ -537,19 +548,22 @@ protected override void ClearItems()
537548
protected override void InsertItem(int index, T item)
538549
{
539550
// Check
540-
if (_suspensionContext != null && _suspensionContext.Mode == SuspensionMode.Removing)
551+
var suspensionContext = _suspensionContext;
552+
if (suspensionContext != null && suspensionContext.Mode == SuspensionMode.Removing)
541553
{
542554
throw Log.ErrorAndCreateException<InvalidOperationException>("Adding items is not allowed in mode SuspensionMode.Removing.");
543555
}
544556

557+
bool? removed = suspensionContext?.TryRemoveItemFromOldItems(index, item);
558+
545559
// Call base
546560
base.InsertItem(index, item);
547561

548-
if (_suspensionContext != null && _suspensionContext.Mode == SuspensionMode.Adding)
562+
if (removed != null && !removed.Value)
549563
{
550564
// Remember
551-
_suspensionContext.NewItems.Add(item);
552-
_suspensionContext.NewItemIndices.Add(index);
565+
suspensionContext.NewItems.Add(item);
566+
suspensionContext.NewItemIndices.Add(index);
553567
}
554568
}
555569

@@ -576,22 +590,25 @@ protected override void MoveItem(int oldIndex, int newIndex)
576590
protected override void RemoveItem(int index)
577591
{
578592
// Check
579-
if (_suspensionContext != null && _suspensionContext.Mode == SuspensionMode.Adding)
593+
var suspensionContext = _suspensionContext;
594+
if (suspensionContext != null && suspensionContext.Mode == SuspensionMode.Adding)
580595
{
581596
throw Log.ErrorAndCreateException<InvalidOperationException>("Removing items is not allowed in mode SuspensionMode.Adding.");
582597
}
583598

584599
// Get item
585600
T item = this[index];
586601

602+
bool? removed = suspensionContext?.TryRemoveItemFromNewItems(index, item);
603+
587604
// Call base
588605
base.RemoveItem(index);
589606

590-
if (_suspensionContext != null && _suspensionContext.Mode == SuspensionMode.Removing)
607+
if (removed != null && !removed.Value)
591608
{
592609
// Remember
593-
_suspensionContext.OldItems.Add(item);
594-
_suspensionContext.OldItemIndices.Add(index);
610+
suspensionContext.OldItems.Add(item);
611+
suspensionContext.OldItemIndices.Add(index);
595612
}
596613
}
597614

@@ -610,6 +627,7 @@ protected override void SetItem(int index, T item)
610627
// Call base
611628
base.SetItem(index, item);
612629
}
630+
613631
#endregion Overrides of ObservableCollection
614632

615633
private NotifyRangedCollectionChangedEventArgs CreateEventArgs(NotifyCollectionChangedAction action, IList changedItems = null, IList<int> changedIndices = null)

0 commit comments

Comments
 (0)