I'm going to assume that we can't guarantee that there is only a single instance of a Group
class for each group name. (Else you would be generating columns based on a list of known groups no?)
Here is a class derived from DataGrid
:
public class SeatsGrid : DataGrid, IValueConverter
{
public SeatsGrid()
{
AutoGenerateColumns = false;
}
#region public List<Seats> SeatsList
public List<Seats> SeatsList
{
get { return GetValue(SeatsListProperty) as List<Seats>; }
set { SetValue(SeatsListProperty, value); }
}
public static readonly DependencyProperty SeatsListProperty =
DependencyProperty.Register(
"SeatsList",
typeof(List<Seats>),
typeof(SeatsGrid),
new PropertyMetadata(null, OnSeatsListPropertyChanged));
private static void OnSeatsListPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
SeatsGrid source = d as SeatsGrid;
List<Seats> value = e.NewValue as List<Seats>;
source.OnSeatsListPropertyChanged(value);
}
private void OnSeatsListPropertyChanged(List<Seats> value)
{
ItemsSource = null;
Columns.Clear();
var groups = value
.SelectMany(seats => seats.Values.Keys)
.Select(g => g.Name)
.Distinct();
foreach (var group in groups)
{
DataGridTextColumn col = new DataGridTextColumn();
col.Binding = new Binding()
{
Converter = this,
ConverterParameter = group
};
col.Header = group;
Columns.Add(col);
}
ItemsSource = value;
}
#endregion public List<Seats> SeatsList
object IValueConverter.Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Seats seats = (Seats)value;
string group = (string)parameter;
Group actualGroup = seats.Values.Keys.FirstOrDefault(g => g.Name == group);
if (actualGroup != null)
{
return seats.Values[actualGroup].ToString();
}
else
{
return null;
}
}
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Place an instance of this class in Xaml and assign a List<Seats>
to its SeatsList
property and it generates the columns and renders the rows.
How does it work?
The magic starts in the OnSeatsListPropertyChanged
method. It first gets a list of distinct Group names. It generates a new Text column for each group name setting the header naturally to the group name.
The weird stuff appears when setting the binding for the column. The binding is given a converter which for convience I decided to implement on the SeatsGrid
class as well. The converter parameter is the group name. Since no path is specified the whole Seats
object will be passed to the converter when binding actually occurs.
Now looking at the IValueConverter.Convert
method. It finds an instance (is any) of Group
in the seats that has the same name as the converter parameter. If found uses that Group
as the key to lookup a value to return.
If the Groups were known to be unique per name then the code can be simplified but the principle is the same.
Seats
(each a member of thisList<Seats>
) both have an entry in their respectiveValues
dictionary for a Group that has the name "GroupA". Do they both reference a single instance ofGroup
or do they each have their own different instance ofGroup
that both happen to have the same value forId
andName
? – AnthonyWJones