I have something here that is really catching me off guard.
I have an ObservableCollection of T that is filled with items. I also have an event handler attached to t
If your ObservableCollection
is not getting clear, then you may try this below code. it may help you:
private TestEntities context; // This is your context
context.Refresh(System.Data.Objects.RefreshMode.StoreWins, context.UserTables); // to refresh the object context
I was just going through some of the charting code in the Silverlight and WPF toolkits and noticed that they also solved this problem (in a kind of similar way) ... and I thought I would go ahead and post their solution.
Basically, they also created a derived ObservableCollection and overrode ClearItems, calling Remove on each item being cleared.
Here is the code:
/// <summary>
/// An observable collection that cannot be reset. When clear is called
/// items are removed individually, giving listeners the chance to detect
/// each remove event and perform operations such as unhooking event
/// handlers.
/// </summary>
/// <typeparam name="T">The type of item in the collection.</typeparam>
public class NoResetObservableCollection<T> : ObservableCollection<T>
{
public NoResetObservableCollection()
{
}
/// <summary>
/// Clears all items in the collection by removing them individually.
/// </summary>
protected override void ClearItems()
{
IList<T> items = new List<T>(this);
foreach (T item in items)
{
Remove(item);
}
}
}
To keep it simple why don't you override the ClearItem method and do whatever you want there ie Detach the items from the event.
public class PeopleAttributeList : ObservableCollection<PeopleAttributeDto>, {
{
protected override void ClearItems()
{
Do what ever you want
base.ClearItems();
}
rest of the code omitted
}
Simple, clean, and contain within the collection code.
I had the same issue, and this was my solution. It seems to work. Does anyone see any potential problems with this approach?
// overriden so that we can call GetInvocationList
public override event NotifyCollectionChangedEventHandler CollectionChanged;
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
NotifyCollectionChangedEventHandler collectionChanged = CollectionChanged;
if (collectionChanged != null)
{
lock (collectionChanged)
{
foreach (NotifyCollectionChangedEventHandler handler in collectionChanged.GetInvocationList())
{
try
{
handler(this, e);
}
catch (NotSupportedException ex)
{
// this will occur if this collection is used as an ItemsControl.ItemsSource
if (ex.Message == "Range actions are not supported.")
{
handler(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
else
{
throw ex;
}
}
}
}
}
}
Here are some other useful methods in my class:
public void SetItems(IEnumerable<T> newItems)
{
Items.Clear();
foreach (T newItem in newItems)
{
Items.Add(newItem);
}
NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public void AddRange(IEnumerable<T> newItems)
{
int index = Count;
foreach (T item in newItems)
{
Items.Add(item);
}
NotifyCollectionChangedEventArgs e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new List<T>(newItems), index);
NotifyCollectionChanged(e);
}
public void RemoveRange(int startingIndex, int count)
{
IList<T> oldItems = new List<T>();
for (int i = 0; i < count; i++)
{
oldItems.Add(Items[startingIndex]);
Items.RemoveAt(startingIndex);
}
NotifyCollectionChangedEventArgs e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new List<T>(oldItems), startingIndex);
NotifyCollectionChanged(e);
}
// this needs to be overridden to avoid raising a NotifyCollectionChangedEvent with NotifyCollectionChangedAction.Reset, which our other lists don't support
new public void Clear()
{
RemoveRange(0, Count);
}
public void RemoveWhere(Func<T, bool> criterion)
{
List<T> removedItems = null;
int startingIndex = default(int);
int contiguousCount = default(int);
for (int i = 0; i < Count; i++)
{
T item = Items[i];
if (criterion(item))
{
if (removedItems == null)
{
removedItems = new List<T>();
startingIndex = i;
contiguousCount = 0;
}
Items.RemoveAt(i);
removedItems.Add(item);
contiguousCount++;
}
else if (removedItems != null)
{
NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removedItems, startingIndex));
removedItems = null;
i = startingIndex;
}
}
if (removedItems != null)
{
NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removedItems, startingIndex));
}
}
private void NotifyCollectionChanged(NotifyCollectionChangedEventArgs e)
{
OnPropertyChanged(new PropertyChangedEventArgs("Count"));
OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
OnCollectionChanged(e);
}
Ok, even though I still wish that ObservableCollection behaved as I wished ... the code below is what I ended up doing. Basically, I created a new collection of T called TrulyObservableCollection and overrided the ClearItems method which I then used to raise a Clearing event.
In the code that uses this TrulyObservableCollection, I use this Clearing event to loop through the items that are still in the collection at that point to do the detach on the event that I was wishing to detach from.
Hope this approach helps someone else as well.
public class TrulyObservableCollection<T> : ObservableCollection<T>
{
public event EventHandler<EventArgs> Clearing;
protected virtual void OnClearing(EventArgs e)
{
if (Clearing != null)
Clearing(this, e);
}
protected override void ClearItems()
{
OnClearing(EventArgs.Empty);
base.ClearItems();
}
}
I tackled this one in a slightly different manner as I wanted to register to one event and handle all additions and removals in the event handler. I started off overriding the collection changed event and redirecting reset actions to removal actions with a list of items. This all went wrong as I was using the observable collection as an items source for a collection view and got "Range actions not supported".
I finally created a new event called CollectionChangedRange which acts in the manner I expected the inbuilt version to act.
I can't imagine why this limitation would be allowed and hope that this post at least stops others from going down the dead end that I did.
/// <summary>
/// An observable collection with support for addrange and clear
/// </summary>
/// <typeparam name="T"></typeparam>
[Serializable]
[TypeConverter(typeof(ExpandableObjectConverter))]
public class ObservableCollectionRange<T> : ObservableCollection<T>
{
private bool _addingRange;
[field: NonSerialized]
public event NotifyCollectionChangedEventHandler CollectionChangedRange;
protected virtual void OnCollectionChangedRange(NotifyCollectionChangedEventArgs e)
{
if ((CollectionChangedRange == null) || _addingRange) return;
using (BlockReentrancy())
{
CollectionChangedRange(this, e);
}
}
public void AddRange(IEnumerable<T> collection)
{
CheckReentrancy();
var newItems = new List<T>();
if ((collection == null) || (Items == null)) return;
using (var enumerator = collection.GetEnumerator())
{
while (enumerator.MoveNext())
{
_addingRange = true;
Add(enumerator.Current);
_addingRange = false;
newItems.Add(enumerator.Current);
}
}
OnCollectionChangedRange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, newItems));
}
protected override void ClearItems()
{
CheckReentrancy();
var oldItems = new List<T>(this);
base.ClearItems();
OnCollectionChangedRange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldItems));
}
protected override void InsertItem(int index, T item)
{
CheckReentrancy();
base.InsertItem(index, item);
OnCollectionChangedRange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
}
protected override void MoveItem(int oldIndex, int newIndex)
{
CheckReentrancy();
var item = base[oldIndex];
base.MoveItem(oldIndex, newIndex);
OnCollectionChangedRange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Move, item, newIndex, oldIndex));
}
protected override void RemoveItem(int index)
{
CheckReentrancy();
var item = base[index];
base.RemoveItem(index);
OnCollectionChangedRange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));
}
protected override void SetItem(int index, T item)
{
CheckReentrancy();
var oldItem = base[index];
base.SetItem(index, item);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, oldItem, item, index));
}
}
/// <summary>
/// A read only observable collection with support for addrange and clear
/// </summary>
/// <typeparam name="T"></typeparam>
[Serializable]
[TypeConverter(typeof(ExpandableObjectConverter))]
public class ReadOnlyObservableCollectionRange<T> : ReadOnlyObservableCollection<T>
{
[field: NonSerialized]
public event NotifyCollectionChangedEventHandler CollectionChangedRange;
public ReadOnlyObservableCollectionRange(ObservableCollectionRange<T> list) : base(list)
{
list.CollectionChangedRange += HandleCollectionChangedRange;
}
private void HandleCollectionChangedRange(object sender, NotifyCollectionChangedEventArgs e)
{
OnCollectionChangedRange(e);
}
protected virtual void OnCollectionChangedRange(NotifyCollectionChangedEventArgs args)
{
if (CollectionChangedRange != null)
{
CollectionChangedRange(this, args);
}
}
}