Search code examples
c#wpfxamldata-binding

How to nest ItemsControl with multiple ItemsSource?


I have a ConfigList object with a name and a Dictionary and I need to nest ItemsControls with different ItemsSource.

I tried to do it this way :

<ItemsControl x:Name="TestStep" Grid.Row="0" Grid.Column="0" ItemsSource="{Binding Path=ConfigList }" HorizontalAlignment="Center" VerticalAlignment="Center">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Path=Ictrl.Nom}" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
    <ItemsControl
    ItemsSource="{Binding Path=Param}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{Binding Path=Key}" />
                    <TextBlock Text=" : " />
                    <TextBlock Text="{Binding Path=Value}" />
                </StackPanel>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</ItemsControl>

When I start my application, I've got this error :

System.Windows.Markup.XamlParseException : '' Adding a value to the collection of type 'System.Windows.Controls.ItemCollection' threw an exception. ' line number '25' and line position '14'. '

Internal Exception InvalidOperationException: Invalid operation when ItemsSource is in use. Access and edit items with ItemsControl.ItemsSource.

Any idea what ​​the problem is?


Solution

  • You set an ItemsSource on the ItemsControl. The data template is used to create the controls that display the data. The created items are then put into the Items collection of the ItemsControl. If you add an element to the ItemsControl directly in XAML this will put them into the Items collection, too. Doing both is not allowed. You either specify an ItemsSource or add to Items directly. From the documentation:

    Note that you use either the Items or the ItemsSource property to specify the collection that should be used to generate the content of your ItemsControl. When the ItemsSource property is set, the Items collection is made read-only and fixed-size.

    However, in your case this is not the real issue, because your markup is wrong for what you want to achieve. If you you really intended to nest ItemsControls, you would simply change the data template for the outer ItemsControl to contain another ItemsControl that binds to a collection property within the outer data item. Since there is already a TextBox, you have to use a panel (e.g. StackPanel) to host multiple controls in the template.

    <ItemsControl x:Name="TestStep" Grid.Row="0" Grid.Column="0" ItemsSource="{Binding Path=ConfigList }" HorizontalAlignment="Center" VerticalAlignment="Center">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                    <TextBlock Text="{Binding Path=Ictrl.Nom}" />
                    <ItemsControl ItemsSource="{Binding Path=Param}">
                        <ItemsControl.ItemTemplate>
                            <DataTemplate>
                                <StackPanel Orientation="Horizontal">
                                    <TextBlock Text="{Binding Path=Key}" />
                                    <TextBlock Text=" : " />
                                    <TextBlock Text="{Binding Path=Value}" />
                                </StackPanel>
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>
                    </ItemsControl>
                </StackPanel>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
    

    If you want to have a hierarchical view of your data, using a TreeView might be a better fit.