0

I have a collection of object (Entity) which contains a sub-collection (Result is Double[]).

I want to display my collection of Entities (Entity) in a DataGrid (or others control) and I want to show my sub-collection in the same line (each value in my sub collection corresponding to a column)

Ex [EDIT] :

Entity name | Entity Value | Result1 | Result2 | ... | Result n
_______________________________________________________________
My Name     | MyValue      | 1       | 2       | ... | 3
My Name II  | other Value  | 10%     | 20%     | ... | 30%

I am using actually a solution based on ICustomTypeDescriptor and PropertyDescriptor described in this thread but it's not appropriate because we want to edit the values in DataGrid, and it's very complicated to maintain and to make evolve too.

Community
  • 1
  • 1
rad
  • 1,857
  • 1
  • 20
  • 30
  • Does `ResultN` have the same number of values all the time? – Rachel Jan 06 '12 at 17:27
  • No, all sub-collections Result[] in all the Entity collection have the same number of elements, but this number of elements changes at different times – rad Jan 06 '12 at 17:35

1 Answers1

1

If you have a static number of Result items, then you can turn off AutoGenerateColumns and define your own columns that bind to those values

<DataGrid AutoGenerateColumns="False">
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Name}" />
        <DataGridTextColumn Binding="{Binding Value}" />
        <DataGridTextColumn Binding="{Binding Results[1]}" />
        <DataGridTextColumn Binding="{Binding Results[2]}" />
        ....
    </DataGrid.Columns>
</DataGrid>

If you have a dynamic amount, you can use a DataGridTemplateColumn and display your sub-collection in something like an ItemsControl

<DataGrid AutoGenerateColumns="False">
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Name}" />
        <DataGridTextColumn Binding="{Binding Value}" />
        <DataGridTemplateColumn>
            <DataGridTemplateColumn.ItemTemplate>
                <DataTemplate>
                    <ItemsControl ItemsSource="{Binding Results}">
                        <ItemsControl.ItemsPanel>
                            <ItemsPanelTemplate>
                                <StackPanel Orientation="Horizontal" />
                            </ItemsPanelTemplate>
                        </ItemsControl.ItemsPanel>
                        <ItemsControl.ItemTemplate>
                            <DataTemplate>
                                <TextBox Width="50" Value="{Binding }" />
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>
                    </ItemsControl>
                </DataTemplate>
            </DataGridTemplateColumn.ItemTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>
Rachel
  • 130,264
  • 66
  • 304
  • 490
  • Thank you for the answer, it's interesting solution, but I think it does not fit us, because no header, no DataGrid style for this items (Result[] it's not editable). And i can't dynamically resize each "pseudo-column" of Result[]. – rad Jan 06 '12 at 17:50
  • @rad You could easily add those things. I left them out for simplicity. If going with first example, add `Header="SomeHeader"` to the DataGridTextColumns. You can also set `IsReadOnly="True"` to prevent editing. If going with the 2nd example, I'd suggest using a `Grid` with `GridSplitters` so you can resize your columns, and using `Grid.SharedSizeScope` to make your Header and Columns line up. Since your above comment said the number of items is the same for all items, I'd suggest using the 1st implementation. If the # of items is dynamic, you could also add the columns in a loop on `Loaded`. – Rachel Jan 06 '12 at 17:56
  • In fact, I want a header per value (I edited my post with a better example) thanks – rad Jan 06 '12 at 18:30
  • 1
    @rad Use the first code sample then. If you don't know how many items are going to exist at runtime, but you know the number of items is all the same, then in the `DataGrid.Loaded` event read the first object's values and generate a `DataGridTextColumn` for each item in the array and add it to the DataGrid. – Rachel Jan 06 '12 at 18:39
  • Thank you for your help @Rachel, It remains me just one case to solve, if the number of Results change I have to Reload my control (with the good number of Columns) – rad Jan 09 '12 at 17:30
  • have you any idea how can I regenerate all columns programmatically when the number of results changes ? – rad Jan 12 '12 at 10:15
  • @rad In the DataGrid's `Loaded` event get the first item in it's `ItemsSource`, and loop through the `Results` collection. For each item, create a `DataGridTextColumn`, bind it to `Results[X]`, and add it to the `DataGrid` – Rachel Jan 12 '12 at 12:29
  • This step is done like you said and works well: for the first time I load the DataGrid and I have all my Results columns. But after, I need to change the number of columns Results. I need to recreate these columns (In my UI I can change the range of Results). How can I do it? – rad Jan 12 '12 at 17:01
  • @rad There are many ways to do that and which one you use depends on the design pattern you're using. You could try subscribing to `DataContextChanged`, or subscribe to the first data item's `Results.CollectionChanged` event. Or you could use a messaging system such as MVVM Light's Messenger or Prism's EventAggregator to fire something like a `ResultsCollectionChanged` message, which your View could subscribe to and update the Columns accordingly. – Rachel Jan 12 '12 at 17:14