I have the following custom observable collection (The code is taken in parts from Dean Chalk's blog http://www.deanchalk.me.uk/post/Thread-Safe-Dispatcher-Safe-Observable-Collection-for-WPF.aspx and slightly altered):
public class ThreadSaveObservableCollection <T> : IList<T>, INotifyCollectionChanged {
private IList<T> collection;
private Dispatcher uiDispatcher;
private ReaderWriterLock rwLock;
public ThreadSaveObservableCollection () {
collection = new List<T>();
rwLock = new ReaderWriterLock();
uiDispatcher = Dispatcher.CurrentDispatcher;
}
public void Insert (int index, T item) {
if (Thread.CurrentThread == uiDispatcher.Thread) {
insert_(index, item);
} else {
uiDispatcher.BeginInvoke(new Action<int, T>(insert_), DispatcherPriority.Normal, new object[] {index, item});
}
}
private void insert_ (int index, T item) {
rwLock.AcquireWriterLock(Timeout.Infinite);
collection.Insert(index, item);
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
rwLock.ReleaseWriterLock();
}
public IEnumerator<T> GetEnumerator () {
rwLock.AcquireReaderLock(Timeout.Infinite);
IEnumerator<T> enumerator = collection.GetEnumerator();
rwLock.ReleaseReaderLock();
return enumerator;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator () {
rwLock.AcquireReaderLock(Timeout.Infinite);
IEnumerator<T> enumerator = collection.GetEnumerator();
rwLock.ReleaseReaderLock();
return enumerator;
}
public event NotifyCollectionChangedEventHandler CollectionChanged;
... // the remaining methods of the IList<> interface
}
Further I have a ViewModel which holds an instance of this class:
public class ViewModel {
private ThreadSaveObservableCollection<string> Collection {get; set;}
public ViewModel () {
Collection = new ThreadSaveObservableCollection<string>();
}
public void Insert (string item) {
Collection.Insert(0, item);
}
}
I apply data binding in code-behind because I create the corresponding WPF control (an ordinary List control) with name "LogList" dynamically:
wpfContainer.LogList.ItemsSource = viewModel.Collection;
Everything works quite fine except the fact that the order of items in the wpf list control is reversed with respect to the items in the Collection object of the ViewModel.
With the statement Collection.Insert(0, intem) I expect to add the new item at the top of the list but what I get is the same result as I would use Collection.Add(item).
When I step into the code during runtime I can verify that the items inside my Collection are in the correct order but on the surface inside the wpf list control the order is altered i.e. reversed.
What am I making wrong ?
I guess the problem must be found somewhere around the data binding because it's the 'wire' that connects my ObservableCollection with the wpf control and it seems that a correct order is getting into the wire and an incorrect is leaving it.
Maybe it has something to do with the GetEnumerator() methods of the IList interface since the ItemSource property of the wpf control is awaiting an Enumerator ?
I have no clue and I am really stuck ...
Thank you in advance for any help ...
Can you try to do this:
http://msdn.microsoft.com/en-us/library/ms653208.aspx
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new List<object>() { item }, 0));
I think that this event is the problem.
Couple notes about your code:
Naming: ThreadSafe, not ThreadSave
Race conditions: you're acquiring a lock to call .GetEnumerator. Then releasing the lock, and returning that enumerator. That is not safe, and will throw an exception at runtime if the thread conditions are right. What you should do here is create a copy of the list while under lock, then return an enumerator to that copy.
ReaderWriterLock has some known performance, scalability, and error-prone usage (e.g. re-entrance) concerns. Use ReaderWriterLocksSlim instead.
The whole idea here is to marshal all operations to the UI thread. If everything happens on the UI thread, there's no need for any locking at all.
Finally, rather than re-invent the wheel, I suggest using one of the existing thread-safe ObservableCollections.
Related
I'm building a Windows Phone 8.1 (WinRt) app.
I have an ObservableCollection<object>, i need to add, modify and sort the items of this collection.
when I add an item to this list everything is ok, if i cast one of this object in the list and i edit the property of the object the INotifyPropertyChanged takes care of updating the ui.
but when i sort the list the UI doesn't respect the order of the list.
the only way to update the UI is to use Move(), but as i have found this method is veri resource hungry.
I have tried with LINQ, but as a result the list is ordered, but the element in the UI remain in the same order.
there is any alternative way to sort this list?
this is some code insede my ViewModel
ActiveServices = ActiveServices.Where(x => x is ActiveServiceControlData).OrderByDescending(x => (x as ActiveServiceControlData).NotificationNumber).ToObservableCollection();
private static ObservableCollection<object> activeServices;
public ObservableCollection<object> ActiveServices
{
get { return activeServices; }
set
{
activeServices = value;
RaisePropertyChanged(() => ActiveServices);
}
}
EDIT
My big issue is that in the ObservableCollection there are different types of object, i use this collection as a ItemsSource for a ListView with an ItemTemplateSelector based on the type of the object inside the ObservableCollection, and i need to sort only the element of a specific type.
The proper way to sort ObservableCollection is to extend base ObservableCollection and make use of internal CollectionChanged events.
Your current code recreates whole collection which is inefficient (and your user interface may "blink").
public class SortableObservableCollection<T, TSortKey> : ObservableCollection<T>
{
private readonly Func<T, TKey> _sortByKey;
public SortableObservableCollection(Func<T, TKey> sortByKey)
{
_sortByKey = sortByKey;
}
public void Sort() {
// slow O(n^2) sort but should be good enough because user interface rarely has milion of items
var sortedList = Items.OrderBy(_sortByKey).ToList();
for (int i = 0; i < sortedList.Count; ++i)
{
var actualItemIndex = Items.IndexOf(sortedList[i]);
if (actualItemIndex != i)
Move(actualItemIndex, i);
}
}
}
.. and then just call .Sort();
The above method has big advantage over recreating whole item source - your user interface can react to that in pretty way (animation of item move instead of recreate "blink")
Let's say I have a list in a class which will be used in a multi threading scenario.
public class MyClass
{
List<MyItem> _list= new List<MyItem>();
protected object SyncRoot {
get {
return ((IList)_list).SyncRoot;
}
}
public void Execute1()
{
lock(SyncRoot)
{
foreach(var item in _list) DoSomething(item);
}
}
public void Execute2()
{
Item[] list;
lock(SyncRoot)
{
list=_list.ToArray();
}
for(var i=0;i<list.Length;i++) DoSomething(list[i]);
}
}
The method Execute1 is the 'normal' way to enumerate the list in a thread-safe way. But what about Execute2? Is this approach still thread-safe?
Access to the (copy of the) List is threadsafe in both scenarios. But of course the MyItem elements are not synchronized in any way.
The second form looks a little more expensive but it will allow Add/Remove on the original while the DoSomething()s are running. The array acts like a kind of snapshot, if that matches your requirements it could be useful. Note that you might as well use ToList().
It's safe as long as every other use of _list is also protected with the same lock statement. You are taking exclusive access to the list, copying its contents and then working on the copy (to which you also have exclusive access due to scoping). A bit wasteful at first sight, but a legitimate approach under certain circumstances.
The situation is like this: I have an Observable Collection that has a bunch of objects in it which I then display to a listbox using a binding. Then periodically, I get a message from an external server that gives me a new (or possibly the same) ordering for these objects. Right now, I just clear the observable collection, and add each item back in using the specified ordering.
This doesn't look too nice. Is there a better way to go about doing this? I'd be really awesome if I could somehow get the listbox to reorder and have a nice reordering animation with it, but that might be asking too much.
I thought about adding the ordering as an attribute to each object in the ObservableCollection and then calling a sort on it. Would this look clean? My assumption is that it would be almost the same effect as clearing it and readding everything.
Thanks for any help!
I created a class that inherits from ObservableCollection. This class has a SetItems method where you need to pass in the newly ordered items. Key in this class is that it will suppress the collectionchanged event and thus will not refresh the listbox each time an item is added. It looks better and loads a lot faster.
public class SuperObservableCollection<T> : ObservableCollection<T>
{
public void SetItems(IEnumerable<T> items)
{
suppressOnCollectionChanged = true;
Clear();
AddRange(items);
}
private bool suppressOnCollectionChanged;
public void AddRange(IEnumerable<T> items)
{
suppressOnCollectionChanged = true;
if (items != null)
{
foreach (var item in items)
Add(item);
}
suppressOnCollectionChanged = false;
NotifyCollectionChanged();
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (!suppressOnCollectionChanged)
base.OnCollectionChanged(e);
}
public void NotifyCollectionChanged()
{
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
I was browsing around Microsoft's site, and got caught up reading ObservableCollection<T>.
I came up with a quick scenario in my head, and determined I could actually use something like this.
I made a sample class that inherets from ObservableCollection<T>, as seen here:
public class Ledger : ObservableCollection<LedgerEntry>, IEnumerable
{
private ObservableCollection<LedgerEntry> _items;
private decimal _currentBalance;
public Ledger(IEnumerable<LedgerEntry> items)
: base(items)
{
_items = new ObservableCollection<LedgerEntry>(items);
_currentBalance = 0m;
}
public new IEnumerator GetEnumerator()
{
var enumerator = _items.GetEnumerator();
while (enumerator.MoveNext())
{
var currentItem = enumerator.Current;
_currentBalance += currentItem.Amount;
currentItem.SetBalance(_currentBalance);
yield return currentItem;
}
}
}
Think about a bank account -- I'd like for each LedgerEntry contained within the collection to "know" it's balance.
Any who --
What I'm wondering is this -- given the above example, I see that GetEnumerator() is called once when I bind it to a ListView, and now I'm curious -- how is ListView actually retrieving / viewing the data contained within the collection?
My thought was that it would iterate the enumerable, but apparently I'm incorrect.
Any info on how this works?
Thanks!
If the collection implements IList<T> that will be used to access the items by index instead.
I don't know why I have an IndexOutOfRangeException when I am clearing a System.Collections.Generic.List<T>. Does this make sense?
List<MyObject> listOfMyObject = new List<MyObject>();
listOfMyObject.Clear();
This typically happens if multiple threads are accessing the list simultaneously. If one thread deletes an element while another calls Clear(), this exception can occur.
The "answer" in this case is to synchronize this appropriately, locking around all of your List access.
Edit:
In order to handle this, the simplest method is to encapsulate your list within a custom class, and expose the methods you need, but lock as needed. You'll need to add locking to anything that alters the collection.
This would be a simple option:
public class MyClassCollection
{
// Private object for locking
private readonly object syncObject = new object();
private readonly List<MyObject> list = new List<MyObject>();
public this[int index]
{
get { return list[index]; }
set
{
lock(syncObject) {
list[index] = value;
}
}
}
public void Add(MyObject value)
{
lock(syncObject) {
list.Add(value);
}
}
public void Clear()
{
lock(syncObject) {
list.Clear();
}
}
// Do any other methods you need, such as remove, etc.
// Also, you can make this class implement IList<MyObject>
// or IEnumerable<MyObject>, but make sure to lock each
// of the methods appropriately, in particular, any method
// that can change the collection needs locking
}
Are you sure that that code throws an exception? I have
using System.Collections.Generic;
class MyObject { }
class Program {
static void Main(string[] args) {
List<MyObject> listOfMyObject = new List<MyObject>();
listOfMyObject.Clear();
}
}
and I do not get an exception.
Is your real-life example more complex? Perhaps you have multiple threads simultaneously accessing the list? Can we see a stack trace?
List<T>.Clear is really quite simple. Using Reflector:
public void Clear() {
if (this._size > 0) {
Array.Clear(this._items, 0, this._size);
this._size = 0;
}
this._version++;
}
In the case when the list already empty, that is not going to ever throw an exception. However, if you are modifying the list on another thread, Array.Clear could throw an IndexOutOfRangeException exception. So if another thread removes an item from the list then this._size (the number of items to clear) will be too big.
The documentation doesn't mention any Exception this method throws, your problem is probably elsewhere.
List<T>.Clear