Thread-Safe Observable List for WPF
I'm probably posting this too early; I haven't had a chance to extensively test it yet but I basically just locked every function down, and made any method that actually modifies the list run on the main thread so that notifications can be sent. It ought to work
{
private Dispatcher dispatcher;
private List<T> list;
private object sync;
public ObservableList(Dispatcher dispatcher = null)
{
this.dispatcher = dispatcher ?? Dispatcher.CurrentDispatcher;
this.list = new List<T>();
this.sync = new object();
}
public event NotifyCollectionChangedEventHandler CollectionChanged;
public virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
lock (sync)
{
if (CollectionChanged != null)
{
CollectionChanged(this, e);
}
}
}
public int IndexOf(T item)
{
lock (sync)
{
return list.IndexOf(item);
}
}
public void Insert(int index, T item)
{
if (dispatcher.CheckAccess())
{
lock (sync)
{
list.Insert(index, item);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
}
}
else
{
dispatcher.Invoke(new Action<int, T>(Insert), DispatcherPriority.Send, index, item);
}
}
public void RemoveAt(int index)
{
if (dispatcher.CheckAccess())
{
lock (sync)
{
var item = list[index];
list.RemoveAt(index);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));
}
}
else
{
dispatcher.Invoke(new Action<int>(RemoveAt), DispatcherPriority.Send, index);
}
}
public T this[int index]
{
get
{
lock (sync) { return list[index]; }
}
set
{
lock (sync) { list[index] = value; }
}
}
public void Add(T item)
{
if (dispatcher.CheckAccess())
{
lock (sync)
{
list.Add(item);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
}
}
else
{
dispatcher.Invoke(new Action<T>(Add), DispatcherPriority.Send, item);
}
}
public void Clear()
{
if (dispatcher.CheckAccess())
{
lock (sync)
{
list.Clear();
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
else
{
dispatcher.Invoke(new Action(Clear), DispatcherPriority.Send);
}
}
public bool Contains(T item)
{
lock (sync) { return list.Contains(item); }
}
public void CopyTo(T[] array, int arrayIndex)
{
lock (sync) { list.CopyTo(array, arrayIndex); }
}
public int Count
{
get { lock (sync) { return list.Count; } }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(T item)
{
if (dispatcher.CheckAccess())
{
lock (sync)
{
var index = list.IndexOf(item);
var result = list.Remove(item);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));
return result;
}
}
else
{
return (bool)dispatcher.Invoke(new Func<T, bool>(Remove), DispatcherPriority.Send, item);
}
}
public IEnumerator<T> GetEnumerator()
{
lock (sync)
{
return list.GetEnumerator();
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
You can simply delete the "where T : INotifyPropertyChanged" if you don't like that restriction, but I put it there so that you don't forget that your objects should notify your controls that their properties have changed, so that the GUI gets refreshed properly.
Thread-Safe Observable Priority Queue for WPF
Building on my last post, I realized that you couldn't push elements onto the queue from a worker thread, making it pretty much useless. However, if we dispatch the pushes back to the UI thread, it should work, right?
Here's (what I believe to be) a thread-safe observable priority queue with notifications for use in WPF.
where TValue : INotifyPropertyChanged
where TPriority : IComparable
{
private SortedDictionary<TPriority, Queue<TValue>> dict;
private int count;
private Dispatcher dispatcher;
public int Count { get { return count; } }
public bool Empty { get { return Count == 0; } }
public PriorityQueue(Dispatcher dispatcher = null)
{
this.dispatcher = dispatcher ?? Dispatcher.CurrentDispatcher;
this.count = 0;
this.dict = new SortedDictionary<TPriority, Queue<TValue>>(new ReverseComparer());
}
private class ReverseComparer : IComparer<TPriority>
{
public int Compare(TPriority x, TPriority y) { return y.CompareTo(x); }
}
public virtual void Push(TValue val, TPriority pri = default(TPriority))
{
if (dispatcher.CheckAccess())
{
++count;
if (!dict.ContainsKey(pri)) dict[pri] = new Queue<TValue>();
dict[pri].Enqueue(val);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, val));
}
else
{
dispatcher.Invoke(new Action<TValue, TPriority>(Push), DispatcherPriority.Send, val, pri);
}
}
public virtual TValue Peek()
{
return dict.First().Value.Peek();
}
public virtual TValue Pop()
{
if (dispatcher.CheckAccess())
{
--count;
var pair = dict.First();
var queue = pair.Value;
var val = queue.Dequeue();
if (queue.Count == 0) dict.Remove(pair.Key);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, val));
return val;
}
else
{
return (TValue)dispatcher.Invoke(new Func<TValue>(Pop), DispatcherPriority.Send);
}
}
public event NotifyCollectionChangedEventHandler CollectionChanged;
public virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (CollectionChanged != null)
{
CollectionChanged(this, e);
}
}
public IEnumerator<TValue> GetEnumerator()
{
foreach (var queue in dict.Values)
{
foreach (var value in queue)
{
yield return value;
}
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Read this blog post to find out how this works.
Observable Priority Queue
Just started playing around with WPF in VS 2010. They have this ObservableCollection class which you can bind to your DataGrid or ListControl and then when you add or remove items from it, the control is refreshed automatically. However, I wanted to use my PriorityQueue class that I posted about earlier, so I modified it a bit:
class PriorityQueue<TValue, TPriority> : IEnumerable<TValue>, INotifyCollectionChanged
where TValue : INotifyPropertyChanged
where TPriority : IComparable
{
private SortedDictionary<TPriority, Queue<TValue>> dict = new SortedDictionary<TPriority, Queue<TValue>>(new ReverseComparer());
private int count = 0;
public int Count { get { return count; } }
public bool Empty { get { return Count == 0; } }
private class ReverseComparer : IComparer<TPriority>
{
public int Compare(TPriority x, TPriority y) { return y.CompareTo(x); }
}
public void Enqueue(TValue val, TPriority pri = default(TPriority))
{
++count;
if (!dict.ContainsKey(pri)) dict[pri] = new Queue<TValue>();
dict[pri].Enqueue(val);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, val));
}
public TValue Dequeue()
{
--count;
var pair = dict.First();
var queue = pair.Value;
var val = queue.Dequeue();
if (queue.Count == 0) dict.Remove(pair.Key);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, val));
return val;
}
public event NotifyCollectionChangedEventHandler CollectionChanged;
public virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (CollectionChanged != null)
{
CollectionChanged(this, e);
}
}
public IEnumerator<TValue> GetEnumerator()
{
foreach (var queue in dict.Values)
{
foreach (var value in queue)
{
yield return value;
}
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Priorities are sorted in descending order (higher value = higher priority).
Also discovered that I could just use "yield return" instead of having to write a custom Enumerator class too! Very nice. Especially since I wouldn't have known how to write it
Human-readable file size in C#
{
string[] units = { "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
while(size >= 1024) {
size /= 1024;
++unit;
}
return String.Format("{0:0.#} {1}", size, units[unit]);
}
I made "bytes" a double so you could pass in fractions, which you might get when calculating download speeds (just add something like "/s" after the output).