In this post i ll show how to dynamically create grouped columns. This new feature was introduced in Infragitistcs WPF v16.1. This post is based on the previous one, so if you have not read it yet you might want to do so.
The attached behaviour class is more complex in this scenario to support the hierarchical layout as you will see in the ViewModels section.
Deliverable

View
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<igDP:XamDataGrid DataSource="{Binding OrderViewModels}" GroupByAreaLocation="None" Margin="5"> <igDP:XamDataGrid.FieldLayoutSettings> <igDP:FieldLayoutSettings AutoGenerateFields="False" AllowFieldGroupCollapsing="True"/> </igDP:XamDataGrid.FieldLayoutSettings> <i:Interaction.Behaviors> <!-- Custom Dependency Property --> <local:DynamicGroupedColumnBehavior GroupsInfo="{Binding Path=OrderViewModels}"/> </i:Interaction.Behaviors> </igDP:XamDataGrid> |
View Models
Here we have more nested setup of the view models. Ideally data models come from business or data access layer go through a ViewModelFactory class that accommodates your scenario.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
internal sealed class OrderViewModel : BindableBase { public int Id { get; set; } private string date; public string Date { get { return date; } set { base.SetProperty(ref date, value); } } private IEnumerable<Warehouse> warehouses; public IEnumerable<Warehouse> Warehouses { get { return warehouses; } set { base.SetProperty(ref warehouses, value); } } } internal class Warehouse { public string Name { get; set; } public IEnumerable<OrderItem> OrderItems { get; set; } } internal class OrderItem { public int Id { get; set; } public string Name { get; set; } } |
Behaviour
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
internal class DynamicGroupedColumnBehavior : Behavior<XamDataGrid> { public static readonly DependencyProperty GroupsInfoProperty = DependencyProperty.Register( "GroupsInfo", typeof(ObservableCollection<OrderViewModel>), typeof(DynamicGroupedColumnBehavior), new PropertyMetadata(OnPropertyValueChanged)); public ObservableCollection<OrderViewModel> GroupsInfo { get { return (ObservableCollection<OrderViewModel>)GetValue(GroupsInfoProperty); } set { SetValue(GroupsInfoProperty, value); } } private static void OnPropertyValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var behavior = d as DynamicGroupedColumnBehavior; var newValues = e.NewValue as IList<OrderViewModel>; if (behavior != null && newValues != null) { behavior.OnPropertyChanged(newValues); } } private void OnPropertyChanged(IList<OrderViewModel> orderViewModels) { var fieldLayout = AssociatedObject.FieldLayouts.FirstOrDefault(); if (fieldLayout == null) return; var warehousesByName = orderViewModels .SelectMany(a => a.Warehouses) .GroupBy(b => b.Name) .ToList(); for (int i = 0; i < warehousesByName.Count; i++) { int maximumColumnsPerGroup = warehousesByName[i].Select(a => a.OrderItems).Max(a => a.Count()); var warehoueViewModel = warehousesByName[i].First(); var fieldGroup = GenerateGroup(i, warehoueViewModel, maximumColumnsPerGroup); fieldLayout.FieldItems.Add(fieldGroup); } } private FieldGroup GenerateGroup(int index, Warehouse group, int numberOfColumns) { var fieldGroup = new FieldGroup { Label = group.Name, LabelPresenterStyle = GroupStyle() }; for (int i = 0; i < numberOfColumns; i++) { fieldGroup.Children.Add(GenerateField(index, i)); } return fieldGroup; } private Field GenerateField(int parentIndex, int index) { return new Field { Name = $"Item{index + 1}", Settings = { EditAsType = typeof(string) }, AlternateBinding = new Binding($"Warehouses[{parentIndex}].OrderItems[{index}].Name") }; } private static Style GroupStyle() { var style = new Style(typeof(FieldGroupLabelPresenter)); style.Setters.Add(new Setter(Control.BackgroundProperty, MyBrushesQueue.Dequeue())); style.Setters.Add(new Setter(Control.FontWeightProperty, FontWeights.Bold)); return style; } private static readonly Brush[] MyBrushes = { Brushes.Aqua, Brushes.Coral, Brushes.Thistle, Brushes.CornflowerBlue, Brushes.YellowGreen }; private static readonly Queue<Brush> MyBrushesQueue = new Queue<Brush>(MyBrushes); } |
Source Code