Click here to Skip to main content
13,799,777 members
Click here to Skip to main content
Add your own
alternative version


62 bookmarked
Posted 15 May 2009
Licenced MIT

Binding a ListView to a Data Matrix

, 15 May 2009
Rate this:
Please Sign up or sign in to vote.
Binding a WPF ListView to a DataMatrix with columns determined at runtime



This article mainly shows how to bind a WPF ListView to a DataMatrix (an undefined data source with dynamic columns) where the ListView columns cannot be determined until runtime.

This article assumes that the reader has prior knowledge of WPF Data Binding and Dependency Properties. For more information on these topics, check out the following articles here on the CodeProject.


Most of the work I do consists of churning undefined (but structured) datasets and generating meaningful information and trends - i.e., Data Analysis, Knowledge Discovery, and Trend Analysis. And as such, most of the data I get are never really similar. In situations like these, you need to come up with a generic way of doing basic stuff like data presentations (reporting) for any kind of dataset. This led me to create a DataMatrix a long time ago. But as we all know, it is not very obvious how to handle anonymous data in WPF.

I know I could have just used a regular DataTable and bound it to the WPF DataGrid, but I can assure you, when you deal with millions of records that cannot be virutalized, a DataTable becomes very heavy, hogs memory, and results in too much messy code [behind] with "Magic" strings all over the place. Besides, we have to keep things as M-V-VM as possible.

Using the Code

Basically, what we are going to do is bind a ListView to a DataMatrix class as shown below:

public class DataMatrix : IEnumerable
    public List<MatrixColumn> Columns { get; set; }
    public List<object[]> Rows { get; set; } 

    IEnumerator IEnumerable.GetEnumerator()
        return new GenericEnumerator(Rows.ToArray());

public class MatrixColumn
    public string Name { get; set; }
    public string StringFormat { get; set; }

Note that the MatrixColumn can always be extended with all the properties you need to format the GridViewColumns.

To achieve this, we need to create a DependencyProperty which will be added to the ListView so that when the DataMatrixSource is set, we can then bind the undefined columns to the ListView at runtime. Below is the class that contains the DependencyProperty.

public class ListViewExtension
    public static readonly DependencyProperty MatrixSourceProperty =
        typeof(DataMatrix), typeof(ListViewExtension),
            new  FrameworkPropertyMetadata(null,
                new  PropertyChangedCallback(OnMatrixSourceChanged)));

    public static DataMatrix GetMatrixSource(DependencyObject d)
        return (DataMatrix)d.GetValue(MatrixSourceProperty);

    public static void SetMatrixSource(DependencyObject d, DataMatrix value)
        d.SetValue(MatrixSourceProperty, value);

    private static void OnMatrixSourceChanged(DependencyObject d,
        DependencyPropertyChangedEventArgs e)
        ListView listView = d as ListView;
        DataMatrix dataMatrix = e.NewValue as DataMatrix;

        listView.ItemsSource = dataMatrix; 
        GridView gridView = listView.View as GridView; 
        int count = 0; 
        foreach (var col in dataMatrix.Columns)
                new  GridViewColumn
                        Header = col.Name,
                        DisplayMemberBinding = new Binding(stringM.Format("[{0}]", count))

This will then be attached to the ListView as shown below:

<ListView cc:ListViewExtension.MatrixSource="{Binding MyDataMatrix}"/>

And that's all. At runtime, your columns will be added dynamically to the ListView's GridView. Note that the ListView's ItemsSource property expects an IEnumerable as the binding element; that is why the matrix implements this interface. This is provided by the GenericEnumerator shown below:

class GenericEnumerator : IEnumerator
    private readonly object[] _list;
    // Enumerators are positioned before the first element
    // until the first MoveNext() call. 

    private int _position = -1;

    public GenericEnumerator(object[] list)
        _list = list;

    public bool MoveNext()
        return (_position < _list.Length);

    public void Reset()
        _position = -1;

    public object Current
            try { return _list[_position]; }
            catch (IndexOutOfRangeException) { throw new InvalidOperationException(); }

For this example, I used the generic Northwind database and bound to the Orders table (orders count per month/year); then I built a simple DataMatrix from the cross tabulation of the Year (row source) vs. Months (columns source) against the sum for the number of orders.

Given the Orders table, you can generate and bind to a well-known dataset to get the "Number of Orders per Month" as follows:

var orders = from o in db.Orders
 group o by new { o.OrderDate.Value.Year, o.OrderDate.Value.Month }
     into g
     select new MonthlyOrderCount() { Year = g.Key.Year, 
            Month = g.Key.Month, NumberOfOrders = g.Count() };

Which will result as shown below:

To get a more meaningful dataset from the above query, we need to cross-tabulate the year vs. month to get the total number of orders. So, we can run the dataset through a reporting algorithm and generate a DataMatrix as follows:

Note: This is a fake DataMatrixGenerator. A real one will determine the columns (at runtime) based on the parameters specified.

private static DataMatrix CreateMatrix(IEnumerable<monthlyordercount> orders)
    DataMatrix result = new DataMatrix {Columns = new List<matrixcolumn>(), 
                                        Rows = new List<object[]>()};
    result.Columns.Add(new MatrixColumn() { Name = "Year" });
    for (int i = 0; i < 12; i++)
        result.Columns.Add(new MatrixColumn()
            Name = string.Format("{0:MMM}", new DateTime(2009, i + 1, 1))
    for (int i = 1996; i < 1999; i++)
        object[] row = new object[13];
        row[0] = i;
        for (int j = 1; j <= 12; j++)
            int count = (from o in orders
                         where o.Year == i && o.Month == j
                         select o.NumberOfOrders).Sum();
            row[j] = count;
    return result;

which will result as shown below:

We can then use this result set at runtime to bind dynamically to the list view using the DataMatrix.

Points of Interest

This approach can also be used for generating flow documents.


  • May 15 2009: Initial release.


This article, along with any associated source code and files, is licensed under The MIT License


About the Author

Tawani Anyangwe
United States United States
No Biography provided

You may also be interested in...

Comments and Discussions

QuestionHow can changes to the Data Matrix appear in the ListView [modified] Pin
Kortermand29-Jun-09 5:03
memberKortermand29-Jun-09 5:03 
AnswerRe: How can changes to the Data Matrix appear in the ListView Pin
Tawani Anyangwe1-Jul-09 10:23
memberTawani Anyangwe1-Jul-09 10:23 
This is just a rough suggestion.

Given the helper method below:

<font color="blue">public static class</font> <font color="teal">Helpers</font>
    <font color="blue">public static</font> <font color="teal">IDataMatrix</font> ToDataMatrix(<font color="blue">this</font> <font color="teal">IDataReader</font> dr)
        <font color="blue">int</font> fieldCount = dr.FieldCount;
        <font color="teal">IDataMatrix</font> result = <font color="blue">new</font> <font color="teal">DataMatrix</font>();
        <font color="blue">for</font> (<font color="blue">int</font> i = 0; i < fieldCount; i++)
            result.AddColumn(dr.GetName(i), dr.GetFieldType(i));
        <font color="blue">while</font> (dr.Read())
            <font color="blue">object</font>[] row = <font color="blue">new</font> <font color="blue">object</font>[fieldCount];

            for (<font color="blue">int</font> i = 0; i < dr.FieldCount; i++)
                row[i] = dr.GetValue(i);
        <font color="blue">return</font> result;

You can dynamically create your matrices and bind them [to the ViewModel property] as follows:

<font color="blue">void</font> OnSelectionChange(<font color="blue">string</font> table)
    <font color="blue">string</font> sql = <font color="blue">null</font>;
    <font color="blue">if</font> (table == <font color="firebrick">"orders"</font>)
        sql = <font color="blue">"select * from orders"</font>;
    <font color="blue">else if</font> (table == <font color="firebrick">"customers"</font>)
        sql = <font color="firebrick">"select CustomerID as ID, companyName as Company from customers"</font>;
    <font color="blue">else
        throw new</font> ArgumentOutOfRangeException</font>(table);
    <font color="teal">IDataReader</font> dr = db.ExecuteReader(sql);
    <font color="blue">this</font>.MatrixSource = dr.ToDataMatrix();        

Then the INotifyPropertyChange should handle the currently displayed data in the listview.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web04 | 2.8.181214.1 | Last Updated 15 May 2009
Article Copyright 2009 by Tawani Anyangwe
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid