Index: /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Data.Views/3.3/StringConvertibleArrayView.cs
===================================================================
--- /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Data.Views/3.3/StringConvertibleArrayView.cs	(revision 9307)
+++ /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Data.Views/3.3/StringConvertibleArrayView.cs	(revision 9308)
@@ -23,7 +23,7 @@
 using System.ComponentModel;
 using System.Drawing;
+using System.Linq;
 using System.Text;
 using System.Windows.Forms;
-using HeuristicLab.Common;
 using HeuristicLab.MainForm;
 using HeuristicLab.MainForm.WindowsForms;
@@ -53,6 +53,7 @@
 
     protected override void DeregisterContentEvents() {
-      Content.ItemChanged -= new EventHandler<EventArgs<int>>(Content_ItemChanged);
+      Content.ColumnNamesChanged -= new EventHandler(Content_ColumnNamesChanged);
       Content.Reset -= new EventHandler(Content_Reset);
+      Content.ItemsChanged -= new EventHandler<ArrayValuesChangedEventArgs>(Content_ItemsChanged);
       base.DeregisterContentEvents();
     }
@@ -60,6 +61,7 @@
     protected override void RegisterContentEvents() {
       base.RegisterContentEvents();
-      Content.ItemChanged += new EventHandler<EventArgs<int>>(Content_ItemChanged);
+      Content.ItemsChanged += new EventHandler<ArrayValuesChangedEventArgs>(Content_ItemsChanged);
       Content.Reset += new EventHandler(Content_Reset);
+      Content.ColumnNamesChanged -= new EventHandler(Content_ColumnNamesChanged);
     }
 
@@ -94,17 +96,37 @@
           dataGridView.Rows[i].Cells[0].Value = Content.GetValue(i);
         }
+
         dataGridView.Columns[0].Width = dataGridView.Columns[0].GetPreferredWidth(DataGridViewAutoSizeColumnMode.AllCells, true);
       }
+      UpdateRowHeaders();
+      dataGridView.AutoResizeRowHeadersWidth(DataGridViewRowHeadersWidthSizeMode.AutoSizeToDisplayedHeaders);
       dataGridView.Enabled = true;
     }
 
-    private void Content_ItemChanged(object sender, EventArgs<int> e) {
+    protected virtual void UpdateRowHeaders() {
+      for (int i = 0; i < dataGridView.RowCount; i++) {
+        if (i < Content.ColumnNames.Count())
+          dataGridView.Rows[i].HeaderCell.Value = Content.ColumnNames.ElementAt(i);
+        else
+          dataGridView.Rows[i].HeaderCell.Value = "Column " + (i + 1);
+      }
+    }
+
+    private void Content_ItemsChanged(object sender, ArrayValuesChangedEventArgs e) {
       if (InvokeRequired)
-        Invoke(new EventHandler<EventArgs<int>>(Content_ItemChanged), sender, e);
+        Invoke(new EventHandler<ArrayValuesChangedEventArgs>(Content_ItemsChanged), sender, e);
       else {
-        dataGridView.Rows[e.Value].Cells[0].Value = Content.GetValue(e.Value);
-        Size size = dataGridView.Rows[e.Value].Cells[0].PreferredSize;
-        dataGridView.Columns[0].Width = Math.Max(dataGridView.Columns[0].Width, size.Width);
-      }
+        foreach (var index in e.Value) {
+          dataGridView.Rows[index].Cells[0].Value = Content.GetValue(index);
+          Size size = dataGridView.Rows[index].Cells[0].PreferredSize;
+          dataGridView.Columns[0].Width = Math.Max(dataGridView.Columns[0].Width, size.Width);
+        }
+      }
+    }
+    private void Content_ColumnNamesChanged(object sender, EventArgs e) {
+      if (InvokeRequired)
+        Invoke(new EventHandler(Content_ColumnNamesChanged), sender, e);
+      else
+        UpdateRowHeaders();
     }
     private void Content_Reset(object sender, EventArgs e) {
Index: /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Data/3.3/BoolArray.cs
===================================================================
--- /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Data/3.3/BoolArray.cs	(revision 9307)
+++ /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Data/3.3/BoolArray.cs	(revision 9308)
@@ -20,4 +20,7 @@
 #endregion
 
+using System;
+using System.Collections.Generic;
+using System.Linq;
 using System.Text;
 using HeuristicLab.Common;
@@ -28,5 +31,5 @@
   [Item("BoolArray", "Represents an array of boolean values.")]
   [StorableClass]
-  public class BoolArray : ValueTypeArray<bool>, IStringConvertibleArray {
+  public class BoolArray : ValueTypeArray<bool> {
     [StorableConstructor]
     protected BoolArray(bool deserializing) : base(deserializing) { }
@@ -42,5 +45,5 @@
     }
 
-    protected virtual bool Validate(string value, out string errorMessage) {
+    protected override bool Validate(string value, out string errorMessage) {
       bool val;
       bool valid = bool.TryParse(value, out val);
@@ -55,8 +58,9 @@
       return valid;
     }
-    protected virtual string GetValue(int index) {
+    protected override string GetValue(int index) {
       return this[index].ToString();
     }
-    protected virtual bool SetValue(string value, int index) {
+    protected override bool SetValue(string value, int index) {
+      if (ReadOnly) throw new NotSupportedException("Item cannot be set. ValueTypeArray is read-only.");
       bool val;
       if (bool.TryParse(value, out val)) {
@@ -68,19 +72,19 @@
     }
 
-    #region IStringConvertibleArray Members
-    int IStringConvertibleArray.Length {
-      get { return Length; }
-      set { Length = value; }
+    protected override bool SetValues(IEnumerable<ArrayValue<string>> arrayValues) {
+      if (ReadOnly) throw new NotSupportedException("Item cannot be set. ValueTypeArray is read-only.");
+      bool parsed;
+      if (!arrayValues.All(v => bool.TryParse(v.Value, out parsed))) return false;
+      List<int> positions = new List<int>();
+      foreach (var v in arrayValues) {
+        parsed = bool.Parse(v.Value);
+        if (array[v.Index] != parsed) {
+          array[v.Index] = parsed;
+          positions.Add(v.Index);
+        }
+      }
+      OnItemsChanged(positions);
+      return true;
     }
-    bool IStringConvertibleArray.Validate(string value, out string errorMessage) {
-      return Validate(value, out errorMessage);
-    }
-    string IStringConvertibleArray.GetValue(int index) {
-      return GetValue(index);
-    }
-    bool IStringConvertibleArray.SetValue(string value, int index) {
-      return SetValue(value, index);
-    }
-    #endregion
   }
 }
Index: /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Data/3.3/BoolMatrix.cs
===================================================================
--- /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Data/3.3/BoolMatrix.cs	(revision 9307)
+++ /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Data/3.3/BoolMatrix.cs	(revision 9308)
@@ -66,4 +66,5 @@
     }
     protected override bool SetValue(string value, int rowIndex, int columnIndex) {
+      if (ReadOnly) throw new NotSupportedException("Item cannot be set. BoolMatrix is read-only.");
       bool val;
       if (bool.TryParse(value, out val)) {
@@ -86,4 +87,5 @@
         }
       }
+      OnItemsChanged(positions);
       return true;
     }
Index: /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Data/3.3/DoubleArray.cs
===================================================================
--- /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Data/3.3/DoubleArray.cs	(revision 9307)
+++ /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Data/3.3/DoubleArray.cs	(revision 9308)
@@ -20,4 +20,7 @@
 #endregion
 
+using System;
+using System.Collections.Generic;
+using System.Linq;
 using System.Text;
 using HeuristicLab.Common;
@@ -28,5 +31,5 @@
   [Item("DoubleArray", "Represents an array of double values.")]
   [StorableClass]
-  public class DoubleArray : ValueTypeArray<double>, IStringConvertibleArray {
+  public class DoubleArray : ValueTypeArray<double> {
     [StorableConstructor]
     protected DoubleArray(bool deserializing) : base(deserializing) { }
@@ -42,5 +45,5 @@
     }
 
-    protected virtual bool Validate(string value, out string errorMessage) {
+    protected override bool Validate(string value, out string errorMessage) {
       double val;
       bool valid = double.TryParse(value, out val);
@@ -55,8 +58,9 @@
       return valid;
     }
-    protected virtual string GetValue(int index) {
+    protected override string GetValue(int index) {
       return this[index].ToString();
     }
-    protected virtual bool SetValue(string value, int index) {
+    protected override bool SetValue(string value, int index) {
+      if (ReadOnly) throw new NotSupportedException("Item cannot be set. ValueTypeArray is read-only.");
       double val;
       if (double.TryParse(value, out val)) {
@@ -68,19 +72,19 @@
     }
 
-    #region IStringConvertibleArray Members
-    int IStringConvertibleArray.Length {
-      get { return Length; }
-      set { Length = value; }
+    protected override bool SetValues(IEnumerable<ArrayValue<string>> arrayValues) {
+      if (ReadOnly) throw new NotSupportedException("Item cannot be set. ValueTypeArray is read-only.");
+      double parsed;
+      if (!arrayValues.All(v => double.TryParse(v.Value, out parsed))) return false;
+      List<int> positions = new List<int>();
+      foreach (var v in arrayValues) {
+        parsed = double.Parse(v.Value);
+        if (array[v.Index] != parsed) {
+          array[v.Index] = parsed;
+          positions.Add(v.Index);
+        }
+      }
+      OnItemsChanged(positions);
+      return true;
     }
-    bool IStringConvertibleArray.Validate(string value, out string errorMessage) {
-      return Validate(value, out errorMessage);
-    }
-    string IStringConvertibleArray.GetValue(int index) {
-      return GetValue(index);
-    }
-    bool IStringConvertibleArray.SetValue(string value, int index) {
-      return SetValue(value, index);
-    }
-    #endregion
   }
 }
Index: /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Data/3.3/DoubleMatrix.cs
===================================================================
--- /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Data/3.3/DoubleMatrix.cs	(revision 9307)
+++ /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Data/3.3/DoubleMatrix.cs	(revision 9308)
@@ -66,4 +66,5 @@
     }
     protected override bool SetValue(string value, int rowIndex, int columnIndex) {
+      if (ReadOnly) throw new NotSupportedException("Item cannot be set. DoubleMatrix is read-only.");
       double val;
       if (double.TryParse(value, out val)) {
@@ -76,5 +77,4 @@
     protected override bool SetValues(IEnumerable<MatrixValue<string>> matrixValues) {
       if (ReadOnly) throw new NotSupportedException("Item cannot be set. DoubleMatrix is read-only.");
-
       double parsed;
       if (!matrixValues.All(v => double.TryParse(v.Value, out parsed))) return false;
@@ -87,4 +87,5 @@
         }
       }
+      OnItemsChanged(positions);
       return true;
     }
Index: /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Data/3.3/IntArray.cs
===================================================================
--- /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Data/3.3/IntArray.cs	(revision 9307)
+++ /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Data/3.3/IntArray.cs	(revision 9308)
@@ -20,4 +20,7 @@
 #endregion
 
+using System;
+using System.Collections.Generic;
+using System.Linq;
 using System.Text;
 using HeuristicLab.Common;
@@ -28,5 +31,5 @@
   [Item("IntArray", "Represents an array of integer values.")]
   [StorableClass]
-  public class IntArray : ValueTypeArray<int>, IStringConvertibleArray {
+  public class IntArray : ValueTypeArray<int> {
     [StorableConstructor]
     protected IntArray(bool deserializing) : base(deserializing) { }
@@ -42,5 +45,5 @@
     }
 
-    protected virtual bool Validate(string value, out string errorMessage) {
+    protected override bool Validate(string value, out string errorMessage) {
       int val;
       bool valid = int.TryParse(value, out val);
@@ -55,8 +58,9 @@
       return valid;
     }
-    protected virtual string GetValue(int index) {
+    protected override string GetValue(int index) {
       return this[index].ToString();
     }
-    protected virtual bool SetValue(string value, int index) {
+    protected override bool SetValue(string value, int index) {
+      if (ReadOnly) throw new NotSupportedException("Item cannot be set. ValueTypeArray is read-only.");
       int val;
       if (int.TryParse(value, out val)) {
@@ -67,20 +71,19 @@
       }
     }
-
-    #region IStringConvertibleArray Members
-    int IStringConvertibleArray.Length {
-      get { return Length; }
-      set { Length = value; }
+    protected override bool SetValues(IEnumerable<ArrayValue<string>> arrayValues) {
+      if (ReadOnly) throw new NotSupportedException("Item cannot be set. ValueTypeArray is read-only.");
+      int parsed;
+      if (!arrayValues.All(v => int.TryParse(v.Value, out parsed))) return false;
+      List<int> positions = new List<int>();
+      foreach (var v in arrayValues) {
+        parsed = int.Parse(v.Value);
+        if (array[v.Index] != parsed) {
+          array[v.Index] = parsed;
+          positions.Add(v.Index);
+        }
+      }
+      OnItemsChanged(positions);
+      return true;
     }
-    bool IStringConvertibleArray.Validate(string value, out string errorMessage) {
-      return Validate(value, out errorMessage);
-    }
-    string IStringConvertibleArray.GetValue(int index) {
-      return GetValue(index);
-    }
-    bool IStringConvertibleArray.SetValue(string value, int index) {
-      return SetValue(value, index);
-    }
-    #endregion
   }
 }
Index: /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Data/3.3/IntMatrix.cs
===================================================================
--- /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Data/3.3/IntMatrix.cs	(revision 9307)
+++ /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Data/3.3/IntMatrix.cs	(revision 9308)
@@ -66,4 +66,5 @@
     }
     protected override bool SetValue(string value, int rowIndex, int columnIndex) {
+      if (ReadOnly) throw new NotSupportedException("Item cannot be set. IntMatrix is read-only.");
       int val;
       if (int.TryParse(value, out val)) {
@@ -86,4 +87,5 @@
         }
       }
+      OnItemsChanged(positions);
       return true;
     }
Index: /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Data/3.3/Interfaces/IStringConvertibleArray.cs
===================================================================
--- /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Data/3.3/Interfaces/IStringConvertibleArray.cs	(revision 9307)
+++ /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Data/3.3/Interfaces/IStringConvertibleArray.cs	(revision 9308)
@@ -21,4 +21,5 @@
 
 using System;
+using System.Collections.Generic;
 using HeuristicLab.Common;
 
@@ -26,4 +27,5 @@
   public interface IStringConvertibleArray : IContent {
     int Length { get; set; }
+    IEnumerable<string> ColumnNames { get; set; }
 
     bool ReadOnly { get; }
@@ -32,7 +34,23 @@
     string GetValue(int index);
     bool SetValue(string value, int index);
+    bool SetValue(ArrayValue<string> arrayValue);
+    bool SetValues(IEnumerable<ArrayValue<string>> arrayValues);
 
-    event EventHandler<EventArgs<int>> ItemChanged;
+    event EventHandler ColumnNamesChanged;
+    event EventHandler<ArrayValuesChangedEventArgs> ItemsChanged;
     event EventHandler Reset;
   }
+  public class ArrayValuesChangedEventArgs : EventArgs<IEnumerable<int>> {
+    public ArrayValuesChangedEventArgs(IEnumerable<int> indices) : base(indices) { }
+  }
+  public struct ArrayValue<T> {
+    public int Index { get; private set; }
+    public T Value { get; private set; }
+
+    public ArrayValue(int index, T value)
+      : this() {
+      Index = index;
+      Value = value;
+    }
+  }
 }
Index: /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Data/3.3/StringArray.cs
===================================================================
--- /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Data/3.3/StringArray.cs	(revision 9307)
+++ /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Data/3.3/StringArray.cs	(revision 9308)
@@ -41,4 +41,20 @@
     protected string[] array;
 
+    [Storable]
+    protected List<string> columnNames;
+    public virtual IEnumerable<string> ColumnNames {
+      get { return this.columnNames; }
+      set {
+        if (ReadOnly) throw new NotSupportedException("ColumnNames cannot be set. ValueTypeArray is read-only.");
+        if (value == null || value.Count() == 0)
+          columnNames = new List<string>();
+        else if (value.Count() != Length)
+          throw new ArgumentException("A column name must be specified for each column.");
+        else
+          columnNames = new List<string>(value);
+        OnColumnNamesChanged();
+      }
+    }
+
     public virtual int Length {
       get { return array.Length; }
@@ -47,4 +63,9 @@
         if (value != Length) {
           Array.Resize<string>(ref array, value);
+          while (columnNames.Count > value)
+            columnNames.RemoveAt(columnNames.Count - 1);
+          while (columnNames.Count < value)
+            columnNames.Add("Column " + columnNames.Count);
+          OnColumnNamesChanged();
           OnReset();
         }
@@ -58,5 +79,5 @@
           if ((value != null) || (array[index] != string.Empty)) {
             array[index] = value != null ? value : string.Empty;
-            OnItemChanged(index);
+            OnItemsChanged(new List<int>(1) { index });
           }
         }
@@ -68,4 +89,13 @@
     public virtual bool ReadOnly {
       get { return readOnly; }
+    }
+
+    [StorableHook(HookType.AfterDeserialization)]
+    private void AfterDeserialization() {
+      if (columnNames == null) {
+        columnNames = new List<string>(array.Length);
+        while (columnNames.Count < array.Length)
+          columnNames.Add("Column " + columnNames.Count);
+      }
     }
 
@@ -75,8 +105,10 @@
       : base(original, cloner) {
       this.array = (string[])original.array.Clone();
+      this.columnNames = new List<string>(original.columnNames);
       this.readOnly = original.readOnly;
     }
     public StringArray() {
       array = new string[0];
+      columnNames = new List<string>();
       readOnly = false;
     }
@@ -85,4 +117,5 @@
       for (int i = 0; i < array.Length; i++)
         array[i] = string.Empty;
+      columnNames = new List<string>();
       readOnly = false;
     }
@@ -92,4 +125,5 @@
       for (int i = 0; i < array.Length; i++)
         array[i] = elements[i] == null ? string.Empty : elements[i];
+      columnNames = new List<string>();
       readOnly = false;
     }
@@ -138,4 +172,5 @@
     }
     protected virtual bool SetValue(string value, int index) {
+      if (ReadOnly) throw new NotSupportedException("Item cannot be set. StringArray is read-only.");
       if (value != null) {
         this[index] = value;
@@ -145,9 +180,28 @@
       }
     }
-
-    public event EventHandler<EventArgs<int>> ItemChanged;
-    protected virtual void OnItemChanged(int index) {
-      if (ItemChanged != null)
-        ItemChanged(this, new EventArgs<int>(index));
+    public virtual bool SetValues(IEnumerable<ArrayValue<string>> arrayValues) {
+      if (ReadOnly) throw new NotSupportedException("Item cannot be set. StringArray is read-only.");
+      if (!arrayValues.All(v => v.Value != null)) return false;
+      List<int> positions = new List<int>();
+      foreach (var v in arrayValues) {
+        if (array[v.Index] != v.Value) {
+          array[v.Index] = v.Value;
+          positions.Add(v.Index);
+        }
+      }
+      OnItemsChanged(positions);
+      return true;
+    }
+
+    public event EventHandler ColumnNamesChanged;
+    protected virtual void OnColumnNamesChanged() {
+      EventHandler handler = ColumnNamesChanged;
+      if (handler != null)
+        handler(this, EventArgs.Empty);
+    }
+    public event EventHandler<ArrayValuesChangedEventArgs> ItemsChanged;
+    protected virtual void OnItemsChanged(IEnumerable<int> indices) {
+      if (ItemsChanged != null)
+        ItemsChanged(this, new ArrayValuesChangedEventArgs(indices));
       OnToStringChanged();
     }
@@ -173,4 +227,10 @@
       return SetValue(value, index);
     }
+    bool IStringConvertibleArray.SetValue(ArrayValue<string> arrayValue) {
+      return SetValue(arrayValue.Value, arrayValue.Index);
+    }
+    bool IStringConvertibleArray.SetValues(IEnumerable<ArrayValue<string>> arrayValues) {
+      return SetValues(arrayValues);
+    }
     #endregion
   }
Index: /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Data/3.3/StringMatrix.cs
===================================================================
--- /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Data/3.3/StringMatrix.cs	(revision 9307)
+++ /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Data/3.3/StringMatrix.cs	(revision 9308)
@@ -243,4 +243,5 @@
     }
     protected virtual bool SetValue(string value, int rowIndex, int columnIndex) {
+      if (ReadOnly) throw new NotSupportedException("Item cannot be set. StringMatrix is read-only.");
       if (value != null) {
         this[rowIndex, columnIndex] = value;
Index: /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Data/3.3/ValueTypeArray.cs
===================================================================
--- /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Data/3.3/ValueTypeArray.cs	(revision 9307)
+++ /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Data/3.3/ValueTypeArray.cs	(revision 9308)
@@ -33,5 +33,5 @@
   [Item("ValueTypeArray", "An abstract base class for representing arrays of value types.")]
   [StorableClass]
-  public abstract class ValueTypeArray<T> : Item, IEnumerable<T> where T : struct {
+  public abstract class ValueTypeArray<T> : Item, IEnumerable<T>, IStringConvertibleArray where T : struct {
     public static new Image StaticItemImage {
       get { return HeuristicLab.Common.Resources.VSImageLibrary.Class; }
@@ -40,4 +40,20 @@
     [Storable]
     protected T[] array;
+
+    [Storable]
+    protected List<string> columnNames;
+    public virtual IEnumerable<string> ColumnNames {
+      get { return this.columnNames; }
+      set {
+        if (ReadOnly) throw new NotSupportedException("ColumnNames cannot be set. ValueTypeArray is read-only.");
+        if (value == null || value.Count() == 0)
+          columnNames = new List<string>();
+        else if (value.Count() != Length)
+          throw new ArgumentException("A column name must be specified for each column.");
+        else
+          columnNames = new List<string>(value);
+        OnColumnNamesChanged();
+      }
+    }
 
     public virtual int Length {
@@ -49,4 +65,9 @@
         if (value != Length) {
           Array.Resize<T>(ref array, value);
+          while (columnNames.Count > value)
+            columnNames.RemoveAt(columnNames.Count - 1);
+          while (columnNames.Count < value)
+            columnNames.Add("Column " + columnNames.Count);
+          OnColumnNamesChanged();
           OnReset();
         }
@@ -60,7 +81,19 @@
         if (!value.Equals(array[index])) {
           array[index] = value;
-          OnItemChanged(index);
+          OnItemsChanged(new List<int>(1) { index });
         }
       }
+    }
+
+    public virtual void SetValues(IEnumerable<ArrayValue<T>> arrayValues) {
+      if (ReadOnly) throw new NotSupportedException("Item cannot be set. ValueTypeMatrix is read-only.");
+      List<int> positions = new List<int>();
+      foreach (var arrayValue in arrayValues) {
+        if (!arrayValue.Value.Equals(array[arrayValue.Index])) {
+          array[arrayValue.Index] = arrayValue.Value;
+          positions.Add(arrayValue.Index);
+        }
+      }
+      OnItemsChanged(positions);
     }
 
@@ -69,4 +102,13 @@
     public virtual bool ReadOnly {
       get { return readOnly; }
+    }
+
+    [StorableHook(HookType.AfterDeserialization)]
+    private void AfterDeserialization() {
+      if (columnNames == null) {
+        columnNames = new List<string>(array.Length);
+        while (columnNames.Count < array.Length)
+          columnNames.Add("Column " + columnNames.Count);
+      }
     }
 
@@ -76,12 +118,15 @@
       : base(original, cloner) {
       this.array = (T[])original.array.Clone();
+      this.columnNames = new List<string>(original.columnNames);
       this.readOnly = original.readOnly;
     }
     protected ValueTypeArray() {
       array = new T[0];
+      columnNames = new List<string>();
       readOnly = false;
     }
     protected ValueTypeArray(int length) {
       array = new T[length];
+      columnNames = new List<string>(length);
       readOnly = false;
     }
@@ -89,4 +134,5 @@
       if (elements == null) throw new ArgumentNullException();
       array = (T[])elements.Clone();
+      columnNames = new List<string>(elements.Length);
       readOnly = false;
     }
@@ -118,8 +164,14 @@
     }
 
-    public event EventHandler<EventArgs<int>> ItemChanged;
-    protected virtual void OnItemChanged(int index) {
-      if (ItemChanged != null)
-        ItemChanged(this, new EventArgs<int>(index));
+    public event EventHandler ColumnNamesChanged;
+    protected virtual void OnColumnNamesChanged() {
+      EventHandler handler = ColumnNamesChanged;
+      if (handler != null)
+        handler(this, EventArgs.Empty);
+    }
+    public event EventHandler<ArrayValuesChangedEventArgs> ItemsChanged;
+    protected virtual void OnItemsChanged(IEnumerable<int> indices) {
+      if (ItemsChanged != null)
+        ItemsChanged(this, new ArrayValuesChangedEventArgs(indices));
       OnToStringChanged();
     }
@@ -130,4 +182,31 @@
       OnToStringChanged();
     }
+
+    protected abstract bool Validate(string value, out string errorMessage);
+    protected abstract string GetValue(int index);
+    protected abstract bool SetValue(string value, int index);
+    protected abstract bool SetValues(IEnumerable<ArrayValue<string>> arrayValues);
+
+    #region IStringConvertibleArray Members
+    int IStringConvertibleArray.Length {
+      get { return Length; }
+      set { Length = value; }
+    }
+    bool IStringConvertibleArray.Validate(string value, out string errorMessage) {
+      return Validate(value, out errorMessage);
+    }
+    string IStringConvertibleArray.GetValue(int index) {
+      return GetValue(index);
+    }
+    bool IStringConvertibleArray.SetValue(string value, int index) {
+      return SetValue(value, index);
+    }
+    bool IStringConvertibleArray.SetValue(ArrayValue<string> arrayValue) {
+      return SetValue(arrayValue.Value, arrayValue.Index);
+    }
+    bool IStringConvertibleArray.SetValues(IEnumerable<ArrayValue<string>> arrayValues) {
+      return SetValues(arrayValues);
+    }
+    #endregion
   }
 }
Index: /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Problems.TestFunctions/3.3/SingleObjectiveTestFunctionSolution.cs
===================================================================
--- /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Problems.TestFunctions/3.3/SingleObjectiveTestFunctionSolution.cs	(revision 9307)
+++ /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Problems.TestFunctions/3.3/SingleObjectiveTestFunctionSolution.cs	(revision 9308)
@@ -204,17 +204,17 @@
 
     private void RegisterBestKnownRealVectorEvents() {
-      BestKnownRealVector.ItemChanged += new EventHandler<EventArgs<int>>(BestKnownRealVector_ItemChanged);
+      BestKnownRealVector.ItemsChanged += new EventHandler<ArrayValuesChangedEventArgs>(BestKnownRealVector_ItemChanged);
       BestKnownRealVector.Reset += new EventHandler(BestKnownRealVector_Reset);
     }
     private void DeregisterBestKnownRealVectorEvents() {
-      BestKnownRealVector.ItemChanged -= new EventHandler<EventArgs<int>>(BestKnownRealVector_ItemChanged);
+      BestKnownRealVector.ItemsChanged -= new EventHandler<ArrayValuesChangedEventArgs>(BestKnownRealVector_ItemChanged);
       BestKnownRealVector.Reset -= new EventHandler(BestKnownRealVector_Reset);
     }
     private void RegisterBestRealVectorEvents() {
-      BestRealVector.ItemChanged += new EventHandler<EventArgs<int>>(BestRealVector_ItemChanged);
+      BestRealVector.ItemsChanged += new EventHandler<ArrayValuesChangedEventArgs>(BestRealVector_ItemChanged);
       BestRealVector.Reset += new EventHandler(BestRealVector_Reset);
     }
     private void DeregisterBestRealVectorEvents() {
-      BestRealVector.ItemChanged -= new EventHandler<EventArgs<int>>(BestRealVector_ItemChanged);
+      BestRealVector.ItemsChanged -= new EventHandler<ArrayValuesChangedEventArgs>(BestRealVector_ItemChanged);
       BestRealVector.Reset -= new EventHandler(BestRealVector_Reset);
     }
@@ -244,5 +244,5 @@
     }
 
-    private void BestKnownRealVector_ItemChanged(object sender, EventArgs<int> e) {
+    private void BestKnownRealVector_ItemChanged(object sender, ArrayValuesChangedEventArgs e) {
       OnBestKnownRealVectorChanged();
     }
@@ -250,5 +250,5 @@
       OnBestKnownRealVectorChanged();
     }
-    private void BestRealVector_ItemChanged(object sender, EventArgs<int> e) {
+    private void BestRealVector_ItemChanged(object sender, ArrayValuesChangedEventArgs e) {
       OnBestRealVectorChanged();
     }
Index: /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Problems.TravelingSalesman/3.3/PathTSPTour.cs
===================================================================
--- /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Problems.TravelingSalesman/3.3/PathTSPTour.cs	(revision 9307)
+++ /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Problems.TravelingSalesman/3.3/PathTSPTour.cs	(revision 9308)
@@ -152,9 +152,9 @@
     }
     private void RegisterPermutationEvents() {
-      Permutation.ItemChanged += new EventHandler<EventArgs<int>>(Permutation_ItemChanged);
+      Permutation.ItemsChanged += new EventHandler<ArrayValuesChangedEventArgs>(Permutation_ItemChanged);
       Permutation.Reset += new EventHandler(Permutation_Reset);
     }
     private void DeregisterPermutationEvents() {
-      Permutation.ItemChanged -= new EventHandler<EventArgs<int>>(Permutation_ItemChanged);
+      Permutation.ItemsChanged -= new EventHandler<ArrayValuesChangedEventArgs>(Permutation_ItemChanged);
       Permutation.Reset -= new EventHandler(Permutation_Reset);
     }
@@ -172,5 +172,5 @@
       OnCoordinatesChanged();
     }
-    private void Permutation_ItemChanged(object sender, EventArgs<int> e) {
+    private void Permutation_ItemChanged(object sender, ArrayValuesChangedEventArgs e) {
       OnPermutationChanged();
     }
Index: /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Problems.VehicleRouting/3.3/VehicleRoutingProblem.cs
===================================================================
--- /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Problems.VehicleRouting/3.3/VehicleRoutingProblem.cs	(revision 9307)
+++ /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Problems.VehicleRouting/3.3/VehicleRoutingProblem.cs	(revision 9308)
@@ -297,5 +297,5 @@
     }
     void DemandParameter_ValueChanged(object sender, EventArgs e) {
-      Demand.ItemChanged += new EventHandler<EventArgs<int>>(Demand_ItemChanged);
+      Demand.ItemsChanged += new EventHandler<ArrayValuesChangedEventArgs>(Demand_ItemChanged);
       Demand.Reset += new EventHandler(Demand_Reset);
       BestKnownSolution = null;
@@ -304,9 +304,9 @@
       BestKnownSolution = null;
     }
-    void Demand_ItemChanged(object sender, EventArgs<int> e) {
+    void Demand_ItemChanged(object sender, ArrayValuesChangedEventArgs e) {
       BestKnownSolution = null;
     }
     void DueTimeParameter_ValueChanged(object sender, EventArgs e) {
-      DueTime.ItemChanged += new EventHandler<EventArgs<int>>(DueTime_ItemChanged);
+      DueTime.ItemsChanged += new EventHandler<ArrayValuesChangedEventArgs>(DueTime_ItemChanged);
       DueTime.Reset += new EventHandler(DueTime_Reset);
       BestKnownSolution = null;
@@ -315,9 +315,9 @@
       BestKnownSolution = null;
     }
-    void DueTime_ItemChanged(object sender, EventArgs<int> e) {
+    void DueTime_ItemChanged(object sender, ArrayValuesChangedEventArgs e) {
       BestKnownSolution = null;
     }
     void ReadyTimeParameter_ValueChanged(object sender, EventArgs e) {
-      ReadyTime.ItemChanged += new EventHandler<EventArgs<int>>(ReadyTime_ItemChanged);
+      ReadyTime.ItemsChanged += new EventHandler<ArrayValuesChangedEventArgs>(ReadyTime_ItemChanged);
       ReadyTime.Reset += new EventHandler(ReadyTime_Reset);
       BestKnownSolution = null;
@@ -326,9 +326,9 @@
       BestKnownSolution = null;
     }
-    void ReadyTime_ItemChanged(object sender, EventArgs<int> e) {
+    void ReadyTime_ItemChanged(object sender, ArrayValuesChangedEventArgs e) {
       BestKnownSolution = null;
     }
     void ServiceTimeParameter_ValueChanged(object sender, EventArgs e) {
-      ServiceTime.ItemChanged += new EventHandler<EventArgs<int>>(ServiceTime_ItemChanged);
+      ServiceTime.ItemsChanged += new EventHandler<ArrayValuesChangedEventArgs>(ServiceTime_ItemChanged);
       ServiceTime.Reset += new EventHandler(ServiceTime_Reset);
       BestKnownSolution = null;
@@ -337,5 +337,5 @@
       BestKnownSolution = null;
     }
-    void ServiceTime_ItemChanged(object sender, EventArgs<int> e) {
+    void ServiceTime_ItemChanged(object sender, ArrayValuesChangedEventArgs e) {
       BestKnownSolution = null;
     }
@@ -392,14 +392,14 @@
       Capacity.ValueChanged += new EventHandler(Capacity_ValueChanged);
       DemandParameter.ValueChanged += new EventHandler(DemandParameter_ValueChanged);
-      Demand.ItemChanged += new EventHandler<EventArgs<int>>(Demand_ItemChanged);
+      Demand.ItemsChanged += new EventHandler<ArrayValuesChangedEventArgs>(Demand_ItemChanged);
       Demand.Reset += new EventHandler(Demand_Reset);
       DueTimeParameter.ValueChanged += new EventHandler(DueTimeParameter_ValueChanged);
-      DueTime.ItemChanged += new EventHandler<EventArgs<int>>(DueTime_ItemChanged);
+      DueTime.ItemsChanged += new EventHandler<ArrayValuesChangedEventArgs>(DueTime_ItemChanged);
       DueTime.Reset += new EventHandler(DueTime_Reset);
       ReadyTimeParameter.ValueChanged += new EventHandler(ReadyTimeParameter_ValueChanged);
-      ReadyTime.ItemChanged += new EventHandler<EventArgs<int>>(ReadyTime_ItemChanged);
+      ReadyTime.ItemsChanged += new EventHandler<ArrayValuesChangedEventArgs>(ReadyTime_ItemChanged);
       ReadyTime.Reset += new EventHandler(ReadyTime_Reset);
       ServiceTimeParameter.ValueChanged += new EventHandler(ServiceTimeParameter_ValueChanged);
-      ServiceTime.ItemChanged += new EventHandler<EventArgs<int>>(ServiceTime_ItemChanged);
+      ServiceTime.ItemsChanged += new EventHandler<ArrayValuesChangedEventArgs>(ServiceTime_ItemChanged);
       ServiceTime.Reset += new EventHandler(ServiceTime_Reset);
       VehiclesParameter.ValueChanged += new EventHandler(VehiclesParameter_ValueChanged);
Index: /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Problems.VehicleRouting/3.4/ProblemInstances/MultiDepotVRP/MDCVRP/MDCVRPTW/MDCVRPPDTW/MDCVRPPDTWProblemInstance.cs
===================================================================
--- /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Problems.VehicleRouting/3.4/ProblemInstances/MultiDepotVRP/MDCVRP/MDCVRPTW/MDCVRPPDTW/MDCVRPPDTWProblemInstance.cs	(revision 9307)
+++ /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Problems.VehicleRouting/3.4/ProblemInstances/MultiDepotVRP/MDCVRP/MDCVRPTW/MDCVRPPDTW/MDCVRPPDTWProblemInstance.cs	(revision 9308)
@@ -125,9 +125,9 @@
     #region Event handlers
     void PickupDeliveryLocationParameter_ValueChanged(object sender, EventArgs e) {
-      PickupDeliveryLocationParameter.Value.ItemChanged += new EventHandler<EventArgs<int>>(Value_ItemChanged);
+      PickupDeliveryLocationParameter.Value.ItemsChanged += new EventHandler<ArrayValuesChangedEventArgs>(Value_ItemChanged);
       EvalBestKnownSolution();
     }
 
-    void Value_ItemChanged(object sender, EventArgs<int> e) {
+    void Value_ItemChanged(object sender, ArrayValuesChangedEventArgs e) {
       EvalBestKnownSolution();
     }
Index: /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Problems.VehicleRouting/3.4/ProblemInstances/SingleDepotVRP/CVRP/CVRPTW/CVRPPDTW/CVRPPDTWProblemInstance.cs
===================================================================
--- /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Problems.VehicleRouting/3.4/ProblemInstances/SingleDepotVRP/CVRP/CVRPTW/CVRPPDTW/CVRPPDTWProblemInstance.cs	(revision 9307)
+++ /branches/ImprovingStringConvertibleMatrix/HeuristicLab.Problems.VehicleRouting/3.4/ProblemInstances/SingleDepotVRP/CVRP/CVRPTW/CVRPPDTW/CVRPPDTWProblemInstance.cs	(revision 9308)
@@ -125,9 +125,9 @@
     #region Event handlers
     void PickupDeliveryLocationParameter_ValueChanged(object sender, EventArgs e) {
-      PickupDeliveryLocationParameter.Value.ItemChanged += new EventHandler<EventArgs<int>>(Value_ItemChanged);
+      PickupDeliveryLocationParameter.Value.ItemsChanged += new EventHandler<ArrayValuesChangedEventArgs>(Value_ItemChanged);
       EvalBestKnownSolution();
     }
 
-    void Value_ItemChanged(object sender, EventArgs<int> e) {
+    void Value_ItemChanged(object sender, ArrayValuesChangedEventArgs e) {
       EvalBestKnownSolution();
     }
