wpfitemscontrol

WPF. How to wrap two ItemsControl´s using same row?


Abstracting, I have two (main) ItemsControl's that draw their content in a wrapping way.
I use WrapPanel etc.
This works well so far. Also in case the window size is changed during runtime.

But I would like to achieve is this:
Imagine first ItemsControl draws 20 items using two rows.
In terms of width, the first row is fully utilized (= "0_xxxx" ... "13_xxxx")
while only (e.g.) 40 % of the second row is used (= "14_xxxx" ... "19_xxxx").

The second ItemsControl starts drawing its content in the (visually counted!) 3rd line.
It therefore does not use the 60% of row 2 (= "14_xxxx" ... "19_xxxx").

ItemsControl #2 should start where ItemsControl #1 has ended.

The following image shows the CURRENT, "wrong" state

enter image description here https://github.com/UweR70/Images/blob/main/WPF_Test_App/2024_04_07__09_03_00.png

The complete code can be found here

https://github.com/UweR70/WPF_Test_App


Solution

  • If I understand correctly what you want to implement, then try this implementation.

    ViewModel:

    using WPF_Test_App.Classes;
    
    namespace WPF_Test_App
    {
        public class OverviewComponentGroupViewModel
        {
            public OverviewComponentGroup TestData { get; }
    
            public OverviewComponentGroupViewModel()
            {
                TestData = new OverviewComponentGroup
                {
                    Id = "Master Label",
                    Components = new List<OverviewComponent> { },
                    ComponentSubGroups = new List<OverviewComponentGroup> { }
                };
    
                // Add some dummy values
                for (var i = 0; i < 20; i++)
                {
                    TestData.Components.Add(new OverviewComponent { Component = $"{i}_xxxx" });
                }
    
                for (int i = 0; i < 5; i++)
                {
                    var components = new List<OverviewComponent>();
                    for (var j = 0; j < 6; j++)
                    {
                        components.Add(new OverviewComponent { Component = $"{i}.{j}_yyyy" });
                    }
    
                    TestData.ComponentSubGroups.Add(
                        new OverviewComponentGroup
                        {
                            Id = $"{i}_SubLabel",
                            Components = components
                        });
                }
            }
        }
    }
    
    <Window x:Class="WPF_Test_App.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:WPF_Test_App"
            xmlns:classes="clr-namespace:WPF_Test_App.Classes"
            Title="MainWindow" Height="450" Width="890"
            DataContext="{DynamicResource vm}">
        <Window.Resources>
            <local:OverviewComponentGroupViewModel x:Key="vm"/>
            <CompositeCollection x:Key="items">
                <CollectionContainer Collection="{Binding TestData.Components, Source={StaticResource vm}}" />
                <CollectionContainer Collection="{Binding TestData.ComponentSubGroups, Source={StaticResource vm}}"/>
            </CompositeCollection>
            
            <DataTemplate DataType="{x:Type classes:OverviewComponent}">
                <Border BorderBrush="Gray" BorderThickness="1" Margin="5" Background="Brown">
                    <TextBlock Text="{Binding Component}" Margin="5" Foreground="White"/>
                </Border>
            </DataTemplate>
    
            <DataTemplate DataType="{x:Type classes:OverviewComponentGroup}">
                <Border BorderBrush="Black" BorderThickness="1" Margin="5"  Background="White">
                    <StackPanel>
                        <Border BorderBrush="Green" BorderThickness="2" Margin="5" Grid.Row="0">
                            <TextBlock Text="{Binding Id}" HorizontalAlignment="Center"/>
                        </Border>
                        <WrapPanel>
                            <ItemsControl ItemsSource="{Binding Components}">
                                <ItemsControl.ItemTemplate>
                                    <DataTemplate>
                                        <Border BorderBrush="Gray" BorderThickness="1" Margin="5">
                                            <TextBlock Text="{Binding Component}" Margin="5"/>
                                        </Border>
                                    </DataTemplate>
                                </ItemsControl.ItemTemplate>
                                <ItemsControl.ItemsPanel>
                                    <ItemsPanelTemplate>
                                        <WrapPanel />
                                    </ItemsPanelTemplate>
                                </ItemsControl.ItemsPanel>
                            </ItemsControl>
                        </WrapPanel>
                    </StackPanel>
                </Border>
            </DataTemplate>
    
            <ItemsPanelTemplate x:Key="itemsPanel">
                <WrapPanel/>
            </ItemsPanelTemplate>
        </Window.Resources>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/> <!-- First row for "Master Label" -->
                <RowDefinition Height="*"/>    <!-- Second row for "Components" and "Component Sub Groups" -->
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
    
            <!-- Master label with green border -->
            <Border BorderBrush="Green" BorderThickness="2" Margin="5" Grid.Row="0">
                <TextBlock Text="{Binding TestData.Id}" HorizontalAlignment="Center"/>
            </Border>
    
            <!-- Main "Components" and "Component Sub Groups" with red border -->
            <Border BorderBrush="Red" BorderThickness="3" Margin="5" Grid.Row="1">
    
                <ItemsControl ItemsSource="{Binding Mode=OneWay, Source={StaticResource items}}" ItemsPanel="{DynamicResource itemsPanel}" />
    
            </Border>
    
        </Grid>
    </Window>
    
    using System.Windows;
    
    namespace WPF_Test_App
    {
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
        }
    }