Introduction
WPF binding is useful and powerful. You can bind anything to ItemsControl.ItemsSource
which inherited from IEnumerable
interface, like one dimension array, List<T>
, Collection<T>
, and so on. But it can bind a 2D array. An exception "Array was not a one-dimensional array" is shown if you bind a multi-dimension array, like 2D array.
Solution 1: Convert to a Class which Inherited from IEnumerable Interface, like DataTable, then Bind it to ListView
Many classes are inherited from IEnumerable
, like List<T>
, Collection<T>
, Dictionary<Key, Value>
and so on. In this solution, I plan to convert it to DataTable
for general use.
Prepare a 2D array first, we generated a 2d array with 100000 * 6.
double[,] data = new double[100000, 6];
for (int i = 0; i < data.GetLength(0); i++)
{
data[i, 0] = i + 1;
for (int j = 1; j < data.GetLength(1); j++)
{
data[i, j] = j + 1;
}
}
We convert this array to a DataTable
, the columns count of DataTable
should equal to the 2nd dimension length of array, in this sample, it was 6.
private DataTable Convert2DArrayToDataTable(double[,] data, string[] columnNames)
{
int len1d = data.GetLength(0);
int len2d = data.GetLength(1);
Check2DArrayMatchColumnNames(data, columnNames);
DataTable dt = new DataTable();
for (int i = 0; i < len2d; i++)
{
dt.Columns.Add(columnNames[i], typeof(double));
}
for (int row = 0; row < len1d; row++)
{
DataRow dr = dt.NewRow();
for (int col = 0; col < len2d; col++)
{
dr[col] = data[row, col];
}
dt.Rows.Add(dr);
}
return dt;
}
private void Check2DArrayMatchColumnNames(double[,] data, string[] columnNames)
{
int len2d = data.GetLength(1);
if (len2d != columnNames.Length)
{
throw new Exception("The second dimensional length must equals column names.");
}
}
What are the columnNames? We used it as field names in DataTable
. It's useful when you are debugging, you can view the DataTable
in DataView
. Next, we initialized the ListView
view and columns.
private void Binding2DArrayToListView
(ListView listview, double[,] data, string[] columnNames)
{
Check2DArrayMatchColumnNames(data, columnNames);
DataTable dt = Convert2DArrayToDataTable(data, columnNames);
GridView gv = new GridView();
for (int i = 0; i < data.GetLength(1); i++)
{
GridViewColumn col = new GridViewColumn();
col.Header = columnNames[i];
col.DisplayMemberBinding = new Binding("[" + i + "]");
gv.Columns.Add(col);
}
lvwArray.View = gv;
lvwArray.ItemsSource = dt.Rows;
}
Now, it's done. But this kind of solution which needs conversion has a performance issue. Conversion will take a long time if the array is a larger one. Whatever converts to any type. What's the better solution? Please continue to solution 2.
Solution 2: Write an Array Visitor, then Bind it to ListView
The most difference between solution 1 and solution 2 are, the solution needs a process conversion. But solution 2 does not need it. We write an array visitor to visit array. Here comes the code:
class ArrayVisitor : IEnumerable<double[]>
{
private double[,] _data;
public ArrayVisitor()
{
}
public ArrayVisitor(double[,] data)
{
_data = data;
}
public double[,] Data
{
get { return _data; }
set { _data = value; }
}
#region IEnumerable<double[]> Members
public IEnumerator<double[]> GetEnumerator()
{
if (_data == null)
throw new ArgumentException("Data cannot be null.", "Data");
int len2d = _data.GetLength(1);
for (int i = 0; i < _data.GetLength(0); i++)
{
double[] arr = new double[len2d];
for (int j = 0; j < len2d; j++)
{
arr[j] = _data[i, j];
}
yield return arr;
}
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
#endregion
}
How to use it in code?
private void Bindng2DArrayToListview2
(ListView listview, double[,] data, string[] columnNames)
{
Check2DArrayMatchColumnNames(data, columnNames);
GridView gv = new GridView();
for (int i = 0; i < data.GetLength(1); i++)
{
GridViewColumn col = new GridViewColumn();
col.Header = columnNames[i];
col.DisplayMemberBinding = new Binding("[" + i + "]");
gv.Columns.Add(col);
}
ArrayVisitor arrayVisitor = new ArrayVisitor(data);
listview.View = gv;
listview.ItemsSource = arrayVisitor;
}
Enjoy the code!
History
- 19th March, 2010: Initial post