I want to bind a collection of objects to a DataGrid in Silverlight. The objects belong to the following type:
public class Seats
{
Dictionary<Group, long> dctValues = new Dictionary<Group, long>();
public int Id { get; set; }
public Dictionary<Group, long> Values
{
开发者_运维知识库 get { return dctValues; }
}
}
Whereas, Group is represented by:
public class Group
{
public int Id { get; set; }
public string Name { get; set; }
}
I want to be able to generate columns based on the dictionary of groups, where each column would have the header set to Group.Name and the cell value for each item equal to the long value in the dictionary.
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.
精彩评论