Program & Design Tips, tricks, tutorials, and tools on programming & web design

7May/100

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 :)

class ObservableList<T> : IList<T>, INotifyCollectionChanged where T : INotifyPropertyChanged
{
    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.

Filed under: C# No Comments
6May/100

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> : PriorityQueue<TValue, int> where TValue : INotifyPropertyChanged { }
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 :)

Tagged as: , , , No Comments
2May/100

Human-readable file size in C#

        static string ReadableFileSize(double size, int unit=0)
        {
            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).

Tagged as: , No Comments
30Apr/100

A Simple Priority Queue in C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace QueueSpace
{
    public class PriorityQueue<TValue> : PriorityQueue<TValue, int> { }

    public class PriorityQueue<TValue, TPriority> where TPriority : IComparable
    {
        private SortedDictionary<TPriority, Queue<TValue>> dict = new SortedDictionary<TPriority, Queue<TValue>>();

        public int Count { get; private set; }
        public bool Empty { get { return Count == 0; } }

        public void Enqueue(TValue val)
        {
            Enqueue(val, default(TPriority));
        }

        public void Enqueue(TValue val, TPriority pri)
        {
            ++Count;
            if (!dict.ContainsKey(pri)) dict[pri] = new Queue<TValue>();
            dict[pri].Enqueue(val);
        }

        public TValue Dequeue()
        {
            --Count;
            var item = dict.Last();
            if (item.Value.Count == 1) dict.Remove(item.Key);
            return item.Value.Dequeue();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var pq = new PriorityQueue<int>();

            for (int i = 0; i < 10; ++i)
            {
                pq.Enqueue(i,i/2);
            }
            while (!pq.Empty)
            {
                Console.Write(pq.Dequeue() + " ");
            }

            Console.ReadKey();
        }
    }
}

Output

8 9 6 7 4 5 2 3 0 1

Items with the same priority are dequeued in the same order that they were inserted.

13Jan/100

.NET Dock Panel

Apparently .NET does not come with any dock widget, like the ones used for the Toolbox and Properties window in Visual Studio. However, there is a freely available one on sourceforge called DockPanel Suite. Unfortunately, it seems to lack any sort of documentation!

This little code snippet below should be enough to get you started though. Just make sure you your form's IsMdiContainer property to True first.

public Form1() {
    InitializeComponent();

    glControl.Load += new System.EventHandler(glControl_Load);
    glControl.Paint += new System.Windows.Forms.PaintEventHandler(glControl_Paint);
    glControl.Dock = DockStyle.Fill;

    var dockPanel = new DockPanel();
    dockPanel.Dock = DockStyle.Fill;
    Controls.Add(dockPanel);
    dockPanel.BringToFront();

    var mainDock = new DockContent();
    mainDock.ShowHint = DockState.Document;
    mainDock.TabText = "untitled1";
    mainDock.Controls.Add(glControl);
    mainDock.Show(dockPanel);

    var propGrid = new PropertyGrid();
    propGrid.Dock = DockStyle.Fill;

    var propertyDock = new DockContent();
    propertyDock.Controls.Add(propGrid);
    propertyDock.ShowHint = DockState.DockRight;
    propertyDock.TabText = "Properties";
    propertyDock.Show(dockPanel);
}
Tagged as: , , No Comments
20Dec/090

C# Blocking Queue

If you're writing a threaded application in C# and you need to wait until a resource becomes available, you can use this class. Very handy for producer/consumer scenarios.

public class BlockingQueue<T>
{
    Queue<T> que = new Queue<T>();
    Semaphore sem = new Semaphore(0, Int32.MaxValue);

    public void Enqueue(T item)
    {
        lock (que)
        {
            que.Enqueue(item);
        }

        sem.Release();
    }

    public T Dequeue()
    {
        sem.WaitOne();

        lock (que)
        {
            return que.Dequeue();
        }
    }
}