Index: /trunk/sources/HeuristicLab.Collections/3.3/HeuristicLab.Collections-3.3.csproj
===================================================================
--- /trunk/sources/HeuristicLab.Collections/3.3/HeuristicLab.Collections-3.3.csproj	(revision 2744)
+++ /trunk/sources/HeuristicLab.Collections/3.3/HeuristicLab.Collections-3.3.csproj	(revision 2745)
@@ -49,4 +49,7 @@
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="ReadOnlyObservableArray.cs" />
+    <Compile Include="ObservableArray.cs" />
+    <Compile Include="IObservableArray.cs" />
     <Compile Include="ObservableKeyedCollection.cs" />
     <Compile Include="ReadOnlyObservableSet.cs" />
Index: /trunk/sources/HeuristicLab.Collections/3.3/IObservableArray.cs
===================================================================
--- /trunk/sources/HeuristicLab.Collections/3.3/IObservableArray.cs	(revision 2745)
+++ /trunk/sources/HeuristicLab.Collections/3.3/IObservableArray.cs	(revision 2745)
@@ -0,0 +1,36 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2008 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.ComponentModel;
+
+namespace HeuristicLab.Collections {
+  public interface IObservableArray<T> : IList<T>, INotifyPropertyChanged {
+    int Length { get; }
+
+    event CollectionItemsChangedEventHandler<IndexedItem<T>> ItemsReplaced;
+    event CollectionItemsChangedEventHandler<IndexedItem<T>> ItemsMoved;
+    event CollectionItemsChangedEventHandler<IndexedItem<T>> CollectionReset;
+  }
+}
Index: /trunk/sources/HeuristicLab.Collections/3.3/ObservableArray.cs
===================================================================
--- /trunk/sources/HeuristicLab.Collections/3.3/ObservableArray.cs	(revision 2745)
+++ /trunk/sources/HeuristicLab.Collections/3.3/ObservableArray.cs	(revision 2745)
@@ -0,0 +1,329 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2008 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Collections {
+  [Serializable]
+  public class ObservableArray<T> : IObservableArray<T> {
+    [Storable]
+    private T[] array;
+
+    #region Properties
+    public int Length {
+      get { return array.Length; }
+    }
+    int ICollection<T>.Count {
+      get { return array.Length; }
+    }
+    bool ICollection<T>.IsReadOnly {
+      get { return array.IsReadOnly; }
+    }
+
+    public T this[int index] {
+      get {
+        return array[index];
+      }
+      set {
+        T item = array[index];
+        if (!((item == null) && (value == null)) && ((item == null) || (!item.Equals(value)))) {
+          array[index] = value;
+          OnItemsReplaced(new IndexedItem<T>[] { new IndexedItem<T>(index, value) }, new IndexedItem<T>[] { new IndexedItem<T>(index, item) });
+          OnPropertyChanged("Item[]");
+        }
+      }
+    }
+    #endregion
+
+    #region Constructors
+    public ObservableArray() {
+      array = new T[0];
+    }
+    public ObservableArray(int length) {
+      array = new T[length];
+    }
+    public ObservableArray(T[] array) {
+      this.array = (T[])array.Clone();
+      OnCollectionReset(GetIndexedItems(), new IndexedItem<T>[0]);
+    }
+    public ObservableArray(IEnumerable<T> collection) {
+      array = collection.ToArray();
+      OnCollectionReset(GetIndexedItems(), new IndexedItem<T>[0]);
+    }
+    #endregion
+
+    #region Access
+    public bool Contains(T item) {
+      return IndexOf(item) != -1;
+    }
+    public int IndexOf(T item) {
+      return Array.IndexOf<T>(array, item);
+    }
+    public int IndexOf(T item, int startIndex) {
+      return Array.IndexOf<T>(array, item, startIndex);
+    }
+    public int IndexOf(T item, int startIndex, int count) {
+      return Array.IndexOf<T>(array, item, startIndex, count);
+    }
+
+    public int LastIndexOf(T item) {
+      return Array.LastIndexOf<T>(array, item);
+    }
+    public int LastIndexOf(T item, int startIndex) {
+      return Array.LastIndexOf<T>(array, item, startIndex);
+    }
+    public int LastIndexOf(T item, int startIndex, int count) {
+      return Array.LastIndexOf<T>(array, item, startIndex, count);
+    }
+
+    public int BinarySearch(T item) {
+      return Array.BinarySearch<T>(array, item);
+    }
+    public int BinarySearch(T item, IComparer<T> comparer) {
+      return Array.BinarySearch<T>(array, item, comparer);
+    }
+    public int BinarySearch(int index, int count, T item) {
+      return Array.BinarySearch<T>(array, index, count, item);
+    }
+    public int BinarySearch(int index, int count, T item, IComparer<T> comparer) {
+      return Array.BinarySearch<T>(array, index, count, item, comparer);
+    }
+
+    public bool Exists(Predicate<T> match) {
+      return Array.Exists<T>(array, match);
+    }
+
+    public T Find(Predicate<T> match) {
+      return Array.Find<T>(array, match);
+    }
+    public T[] FindAll(Predicate<T> match) {
+      return Array.FindAll<T>(array, match);
+    }
+    public T FindLast(Predicate<T> match) {
+      return Array.FindLast<T>(array, match);
+    }
+
+    public int FindIndex(Predicate<T> match) {
+      return Array.FindIndex<T>(array, match);
+    }
+    public int FindIndex(int startIndex, Predicate<T> match) {
+      return Array.FindIndex<T>(array, startIndex, match);
+    }
+    public int FindIndex(int startIndex, int count, Predicate<T> match) {
+      return Array.FindIndex<T>(array, startIndex, count, match);
+    }
+
+    public int FindLastIndex(Predicate<T> match) {
+      return Array.FindLastIndex<T>(array, match);
+    }
+    public int FindLastIndex(int startIndex, Predicate<T> match) {
+      return Array.FindLastIndex<T>(array, startIndex, match);
+    }
+    public int FindLastIndex(int startIndex, int count, Predicate<T> match) {
+      return Array.FindLastIndex<T>(array, startIndex, count, match);
+    }
+    #endregion
+
+    #region Manipulation
+    void ICollection<T>.Add(T item) {
+      throw new NotSupportedException();
+    }
+    void IList<T>.Insert(int index, T item) {
+      throw new NotSupportedException();
+    }
+    bool ICollection<T>.Remove(T item) {
+      throw new NotSupportedException();
+    }
+    void IList<T>.RemoveAt(int index) {
+      throw new NotSupportedException();
+    }
+
+    public void Clear(int index, int length) {
+      if (length > 0) {
+        IndexedItem<T>[] oldItems = GetIndexedItems(index, length);
+        Array.Clear(array, index, length);
+        OnPropertyChanged("Item[]");
+        OnItemsReplaced(GetIndexedItems(index, length), oldItems); 
+      }
+    }
+    void ICollection<T>.Clear() {
+      Clear(0, array.Length);
+    }
+
+    public void Resize(int newSize) {
+      if (newSize != array.Length) {
+        IndexedItem<T>[] oldItems = GetIndexedItems();
+        Array.Resize<T>(ref array, newSize);
+        OnPropertyChanged("Length");
+        OnPropertyChanged("Item[]");
+        OnCollectionReset(GetIndexedItems(), oldItems);
+      }
+    }
+
+    public void Reverse() {
+      if (array.Length > 1) {
+        IndexedItem<T>[] oldItems = GetIndexedItems();
+        Array.Reverse(array);
+        OnPropertyChanged("Item[]");
+        OnItemsMoved(GetIndexedItems(), oldItems);
+      }
+    }
+    public void Reverse(int index, int length) {
+      if (length > 1) {
+        IndexedItem<T>[] oldItems = GetIndexedItems(index, length);
+        Array.Reverse(array, index, length);
+        OnPropertyChanged("Item[]");
+        OnItemsMoved(GetIndexedItems(index, length), oldItems);
+      }
+    }
+
+    public void Sort() {
+      if (array.Length > 1) {
+        IndexedItem<T>[] oldItems = GetIndexedItems();
+        Array.Sort<T>(array);
+        OnPropertyChanged("Item[]");
+        OnItemsMoved(GetIndexedItems(), oldItems);
+      }
+    }
+    public void Sort(Comparison<T> comparison) {
+      if (array.Length > 1) {
+        IndexedItem<T>[] oldItems = GetIndexedItems();
+        Array.Sort<T>(array, comparison);
+        OnPropertyChanged("Item[]");
+        OnItemsMoved(GetIndexedItems(), oldItems);
+      }
+    }
+    public void Sort(IComparer<T> comparer) {
+      if (array.Length > 1) {
+        IndexedItem<T>[] oldItems = GetIndexedItems();
+        Array.Sort<T>(array, comparer);
+        OnPropertyChanged("Item[]");
+        OnItemsMoved(GetIndexedItems(), oldItems);
+      }
+    }
+    public void Sort(int index, int length) {
+      if (length > 1) {
+        IndexedItem<T>[] oldItems = GetIndexedItems(index, length);
+        Array.Sort<T>(array, index, length);
+        OnPropertyChanged("Item[]");
+        OnItemsMoved(GetIndexedItems(index, length), oldItems);
+      }
+    }
+    public void Sort(int index, int length, IComparer<T> comparer) {
+      if (length > 1) {
+        IndexedItem<T>[] oldItems = GetIndexedItems(index, length);
+        Array.Sort<T>(array, index, length, comparer);
+        OnPropertyChanged("Item[]");
+        OnItemsMoved(GetIndexedItems(index, length), oldItems);
+      }
+    }
+    #endregion
+
+    #region Conversion
+    public ReadOnlyObservableArray<T> AsReadOnly() {
+      return new ReadOnlyObservableArray<T>(this);
+    }
+    public void CopyTo(T[] array) {
+      Array.Copy(this.array, array, this.array.Length);
+    }
+    public void CopyTo(T[] array, int arrayIndex) {
+      Array.Copy(this.array, 0, array, arrayIndex, this.array.Length);
+    }
+    public void CopyTo(int index, T[] array, int arrayIndex, int count) {
+      Array.Copy(this.array, index, array, arrayIndex, count);
+    }
+    public TOutput[] ConvertAll<TOutput>(Converter<T, TOutput> converter) {
+      return Array.ConvertAll<T, TOutput>(array, converter);
+    }
+    #endregion
+
+    #region Processing
+    public void ForEach(Action<T> action) {
+      Array.ForEach<T>(array, action);
+    }
+    public bool TrueForAll(Predicate<T> match) {
+      return Array.TrueForAll<T>(array, match);
+    }
+    #endregion
+
+    #region Enumeration
+    public IEnumerator<T> GetEnumerator() {
+      foreach (object o in ((IEnumerable)this))
+        yield return (T)o;
+    }
+    IEnumerator IEnumerable.GetEnumerator() {
+      return array.GetEnumerator();
+    }
+    #endregion
+
+    #region Events
+    [field: NonSerialized]
+    public event CollectionItemsChangedEventHandler<IndexedItem<T>> ItemsReplaced;
+    protected virtual void OnItemsReplaced(IEnumerable<IndexedItem<T>> items, IEnumerable<IndexedItem<T>> oldItems) {
+      if (ItemsReplaced != null)
+        ItemsReplaced(this, new CollectionItemsChangedEventArgs<IndexedItem<T>>(items, oldItems));
+    }
+
+    [field: NonSerialized]
+    public event CollectionItemsChangedEventHandler<IndexedItem<T>> ItemsMoved;
+    protected virtual void OnItemsMoved(IEnumerable<IndexedItem<T>> items, IEnumerable<IndexedItem<T>> oldItems) {
+      if (ItemsMoved != null)
+        ItemsMoved(this, new CollectionItemsChangedEventArgs<IndexedItem<T>>(items, oldItems));
+    }
+
+    [field: NonSerialized]
+    public event CollectionItemsChangedEventHandler<IndexedItem<T>> CollectionReset;
+    protected virtual void OnCollectionReset(IEnumerable<IndexedItem<T>> items, IEnumerable<IndexedItem<T>> oldItems) {
+      if (CollectionReset != null)
+        CollectionReset(this, new CollectionItemsChangedEventArgs<IndexedItem<T>>(items, oldItems));
+    }
+
+    [field: NonSerialized]
+    public event PropertyChangedEventHandler PropertyChanged;
+    protected virtual void OnPropertyChanged(string propertyName) {
+      if (PropertyChanged != null)
+        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+    }
+    #endregion
+
+    #region Private helpers
+    private IndexedItem<T>[] GetIndexedItems() {
+      IndexedItem<T>[] items = new IndexedItem<T>[array.Length];
+      for (int i = 0; i < array.Length; i++)
+        items[i] = new IndexedItem<T>(i, array[i]);
+      return items;
+    }
+    private IndexedItem<T>[] GetIndexedItems(int index, int count) {
+      IndexedItem<T>[] items = new IndexedItem<T>[count];
+      for (int i = 0; i < count; i++)
+        items[i] = new IndexedItem<T>(index + i, array[index + i]);
+      return items;
+    }
+    #endregion
+  }
+}
Index: /trunk/sources/HeuristicLab.Collections/3.3/ObservableList.cs
===================================================================
--- /trunk/sources/HeuristicLab.Collections/3.3/ObservableList.cs	(revision 2744)
+++ /trunk/sources/HeuristicLab.Collections/3.3/ObservableList.cs	(revision 2745)
@@ -58,5 +58,5 @@
       set {
         T item = list[index];
-        if (!item.Equals(value)) {
+        if (!((item == null) && (value == null)) && ((item == null) || (!item.Equals(value)))) {
           list[index] = value;
           OnItemsReplaced(new IndexedItem<T>[] { new IndexedItem<T>(index, value) }, new IndexedItem<T>[] { new IndexedItem<T>(index, item) });
@@ -320,5 +320,5 @@
     }
     public void Sort(int index, int count, IComparer<T> comparer) {
-      if (list.Count > 1) {
+      if (count > 1) {
         IndexedItem<T>[] oldItems = GetIndexedItems(index, count);
         list.Sort(index, count, comparer);
Index: /trunk/sources/HeuristicLab.Collections/3.3/ReadOnlyObservableArray.cs
===================================================================
--- /trunk/sources/HeuristicLab.Collections/3.3/ReadOnlyObservableArray.cs	(revision 2745)
+++ /trunk/sources/HeuristicLab.Collections/3.3/ReadOnlyObservableArray.cs	(revision 2745)
@@ -0,0 +1,158 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2008 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Collections {
+  [Serializable]
+  public class ReadOnlyObservableArray<T> : IObservableArray<T> {
+    [Storable]
+    private IObservableArray<T> array;
+
+    #region Properties
+    public int Length {
+      get { return array.Length; }
+    }
+    int ICollection<T>.Count {
+      get { return array.Count; }
+    }
+    bool ICollection<T>.IsReadOnly {
+      get { return true; }
+    }
+
+    public T this[int index] {
+      get { return array[index]; }
+    }
+    T IList<T>.this[int index] {
+      get { return array[index]; }
+      set { throw new NotSupportedException(); }
+    }
+    #endregion
+
+    #region Constructors
+    public ReadOnlyObservableArray(IObservableArray<T> array) {
+      if (array == null) throw new ArgumentNullException();
+      this.array = array;
+      array.ItemsReplaced += new CollectionItemsChangedEventHandler<IndexedItem<T>>(array_ItemsReplaced);
+      array.ItemsMoved += new CollectionItemsChangedEventHandler<IndexedItem<T>>(array_ItemsMoved);
+      array.CollectionReset += new CollectionItemsChangedEventHandler<IndexedItem<T>>(array_CollectionReset);
+      array.PropertyChanged += new PropertyChangedEventHandler(array_PropertyChanged);
+    }
+    #endregion
+
+    #region Access
+    public bool Contains(T item) {
+      return array.Contains(item);
+    }
+
+    public int IndexOf(T item) {
+      return array.IndexOf(item);
+    }
+    #endregion
+
+    #region Manipulation
+    void ICollection<T>.Add(T item) {
+      throw new NotSupportedException();
+    }
+
+    void IList<T>.Insert(int index, T item) {
+      throw new NotSupportedException();
+    }
+
+    bool ICollection<T>.Remove(T item) {
+      throw new NotSupportedException();
+    }
+    void IList<T>.RemoveAt(int index) {
+      throw new NotSupportedException();
+    }
+
+    void ICollection<T>.Clear() {
+      throw new NotSupportedException();
+    }
+    #endregion
+
+    #region Conversion
+    public void CopyTo(T[] array, int arrayIndex) {
+      this.array.CopyTo(array, arrayIndex);
+    }
+    #endregion
+
+    #region Enumeration
+    public IEnumerator<T> GetEnumerator() {
+      return array.GetEnumerator();
+    }
+    IEnumerator IEnumerable.GetEnumerator() {
+      return array.GetEnumerator();
+    }
+    #endregion
+
+    #region Events
+    [field: NonSerialized]
+    public event CollectionItemsChangedEventHandler<IndexedItem<T>> ItemsReplaced;
+    protected virtual void OnItemsReplaced(IEnumerable<IndexedItem<T>> items, IEnumerable<IndexedItem<T>> oldItems) {
+      if (ItemsReplaced != null)
+        ItemsReplaced(this, new CollectionItemsChangedEventArgs<IndexedItem<T>>(items, oldItems));
+    }
+
+    [field: NonSerialized]
+    public event CollectionItemsChangedEventHandler<IndexedItem<T>> ItemsMoved;
+    protected virtual void OnItemsMoved(IEnumerable<IndexedItem<T>> items, IEnumerable<IndexedItem<T>> oldItems) {
+      if (ItemsMoved != null)
+        ItemsMoved(this, new CollectionItemsChangedEventArgs<IndexedItem<T>>(items, oldItems));
+    }
+
+    [field: NonSerialized]
+    public event CollectionItemsChangedEventHandler<IndexedItem<T>> CollectionReset;
+    protected virtual void OnCollectionReset(IEnumerable<IndexedItem<T>> items, IEnumerable<IndexedItem<T>> oldItems) {
+      if (CollectionReset != null)
+        CollectionReset(this, new CollectionItemsChangedEventArgs<IndexedItem<T>>(items, oldItems));
+    }
+
+    [field: NonSerialized]
+    public event PropertyChangedEventHandler PropertyChanged;
+    protected virtual void OnPropertyChanged(string propertyName) {
+      if (PropertyChanged != null)
+        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+    }
+
+    private void array_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<IndexedItem<T>> e) {
+      OnItemsReplaced(e.Items, e.OldItems);
+    }
+    private void array_ItemsMoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<T>> e) {
+      OnItemsMoved(e.Items, e.OldItems);
+    }
+    private void array_CollectionReset(object sender, CollectionItemsChangedEventArgs<IndexedItem<T>> e) {
+      OnCollectionReset(e.Items, e.OldItems);
+    }
+    private void array_PropertyChanged(object sender, PropertyChangedEventArgs e) {
+      if (e.PropertyName.Equals("Item[]") || e.PropertyName.Equals("Length"))
+        OnPropertyChanged(e.PropertyName);
+    }
+    #endregion
+  }
+}
