I am working on a datagrid, the object I am putting in has multiple properties including a String array.
When binding to the grid you can only get one row whereas i need to be able to have a row per item in the array any help?
I have the data grid working using Command="{Binding PropX}" however it only gives single rows
<DataGridTextColumn Header="Names"
Binding="{Binding Names}"
Width="Auto" />
<DataGridTextColumn Header="ID"
Binding="{Binding ID}"
Width="Auto" />
<DataGridTextColumn Header="Code"
Binding="{Binding Code}"
Width="Auto" />
<DataGridTextColumn Header="Contract"
Binding="{Binding Contract}"
Width="Auto" />
<DataGridTextColumn Header="Wants Data File"
Binding="{Binding WantsDataFile}"
Width="Auto" />
Names in the instance is an array of names, so for each name i need a new row with the rest of the data still the same.
Datagrid is bound to an observable collection of the object
public class Customer : ObservableObject{
public ObservableCollection<string> names {get;set;}
public int ID {get;set;}
public.......
}
THE GRID IS BOUND TO AN OBSERVABLE COLLECTION OF CUSTOMER
CodePudding user response:
Two options.
1. Construct a Flat List of Customer-By-Name View Models
If you want one row per name (not just per Customer) you will need to do a one-to-many transformation because you ultimately need a flat array to provide to ItemsSource
. There are plenty of ways this could be done, but here's one:
public class CustomerByNameViewModel : ObservableObject
{
public CustomerByNameViewModel (Customer customer, int nameIndex)
{
_nameIndex = nameIndex;
this.Customer = customer;
}
// what you bind to
public int ID => Customer.ID;
public string Name => Customer.names[_nameIndex];
// not what you bind to; instead add public
// accessors for any other Customer properties
// you need
internal Customer Customer {get; set;}
private int _nameIndex;
}
In your main view model:
CustomerByNameViewModel[] _FlatCustomerList;
public CustomerByNameViewModel[] FlatCustomerList
{
get
{
if (_FlatCustomerList == null)
{
_FlatCustomerList = Customers.SelectMany(c =>
{
int i = 0;
return c.Select(c2 => new CustomerByNameViewModel(c2, i ));
}).ToArray(); // don't omit this!
}
return _FlatCustomerList;
}
}
Then set ItemsSource
to FlatCustomerList
.
Big drawback to this - you lose the benefits of ObservableCollection
. If the either the collection of Customers
OR their individual names change you need to null
out _FlatCustomerList
and trigger a property changed notification to rebuild the flat list. Not the most efficient thing, but it does do the job.
Edit - I changed this to wrap Customer
in its own view model, which would be a much better practice so as to keep the data concern separated from its visualization, and avoid creating new Customer
objects just to generate the flat list.
If you're feeling ambitious you could extend this further to have the VM listen to changes on the Customer object and update appropriately.
2. Use DataGrid Details
The other option is to use DataGrid
details. In my experience this is rarely used but it's a pretty powerful feature. A very good example is here. The idea is you'd keep your current collection intact (though I would still add a new derived property that at least always gives the first name in the list to use in the main row) and use it to populate the main row.
Then in the RowDetailsTemplate
you can put whatever you want; you have the Customer
as the DataContext
so you could bind Names
to another ItemsControl
like a ListBox
to display all the names. You might find this to be a better design choice than a separate row for each customer name.