4
votes

How would you go about binding a WPF datagrid that needs to display data from many different datasources with varying number of columns with different column headers and types?

What I'm currently doing is creating a custom List<DataGridColumn>() list of datagrid columns in my ViewModel for each different collection of records to be displayed in this DataGrid.

I Loop over this List to set the DataGrid columns:

foreach (DataGridColumn dgc in dgcSample)
{
  dgc.HeaderStyle = hStyle;
  dgMyDataGrid.Columns.Add(dgc);
}

Finally, I use the ItemsSource to set the source of items:

dgMyDataGrid.ItemsSource = SomeCollection;

This works but it is not binding and it breaks the MVVM guideline that ViewModel should be agnostic to specific UI elements since it now has to deal with the DataGrid and host a collection of DataGridColumn objects...

Any thoughts?

3

3 Answers

1
votes

Have you considered using the datagrid derived class shown in http://www.codeproject.com/KB/grid/MultiColumnSetDataGrid.aspx

This will allow you view to define multiple column sets in your view and switch between them.

2
votes

I think you have to bind your DataGrid with one datasource, but your task is just creating this datasource.

I'll create a sample for creating a DataSource from different entities using LINQ..

Assuming you have two different entities: Entity1 and Entity2, and each one has common ID:

class Entity1
{
    public int ID { get; set; }
    public string E1Column { get; set; }
}

class Entity2
{
    public int ID { get; set; }
    public string E2Column { get; set; }
}

You can create the DataSource using Join in LINQ like the following:

List<Entity1> e1List = new List<Entity1>();
e1List.Add(new Entity1() { ID = 1, E1Column = "E1 a" });
e1List.Add(new Entity1() { ID = 2, E1Column = "E1 b" });

List<Entity2> e2List = new List<Entity2>();
e2List.Add(new Entity2() { ID = 1, E2Column = "E2 a" });
e2List.Add(new Entity2() { ID = 2, E2Column = "E2 b" });

var query = from e1 in e1List
            join e2 in e2List on e1.ID equals e2.ID
            select new { ID = e1.ID, E1Column = e1.E1Column, E2Column = e2.E2Column };

// Bind the DataGrid
dataGrid1.ItemsSource = query.ToList();

Good luck!

1
votes

IMO the right way of doing this is to encapsulate all the data sources into a single object, that is similar to your custom List<DataGridColumn>(). Call this MultifunctionalSource.

This new object will contain a the list of the columns, probably with methods to add new sources and aggregate them. Possibly when adding a new source you could automatically manage the list of columns.

MultifunctionalSource is responsible for providing a bindable datasource.

The model will provide objects of type MultifunctionalSource to the View.

Within the view you should have a new control derived from datagrid that will understand how to display objects of type MultifunctionalSource. This new control will probably be quite simple in the first instance as it can simply setup its columns based on the binding.

MultifunctionalSource should probably return a list of columns that it thinks are relevant for display. It should also be able to return a complete list of columns to enable the UI to decide which columns are relevant based on other criteria; thus keeping the boundaries.