Search code examples
c#wpfxaml

Set ListView / WrapPanel Item Height Dynamically For Each Row


I Have a Listview where i want to display a list of objects in a WrapPanel, each object contains another list of strings for a nested ListView which consequentially has a variable height because the lists have different numbers of entries for each object.

Now I want each ListViewItem for the first ListView to have the same height as the one with the greatest height for each row in the WrapPanel so that the ListViewItem's content is stretched over the available space.

Here is the XAML:

<Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <ListView ItemsSource="{Binding List1}" Grid.Column="0" ScrollViewer.HorizontalScrollBarVisibility="Disabled">

            <ListView.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel Orientation="Horizontal" ></WrapPanel>
                </ItemsPanelTemplate>
            </ListView.ItemsPanel>

            <ListView.ItemContainerStyle >
                <Style TargetType="ListViewItem" >
                    <Setter Property="VerticalAlignment" Value="Top" />
                </Style>
            </ListView.ItemContainerStyle>
            
            <ListView.ItemTemplate>
                <DataTemplate>
                    <Expander IsExpanded="True" Width="230" >
                        <Expander.Header>
                            <TextBlock Text="{Binding Title}" FontSize="16" />
                        </Expander.Header>
                        <Border BorderBrush="LightGray" BorderThickness="0.5" >
                            <ListView ItemsSource="{Binding Persons}" >
                                <ListView.ItemTemplate>
                                    <DataTemplate>
                                        <TextBlock Text="{Binding}" Grid.Column="1" Margin="10,0" />
                                    </DataTemplate>
                                </ListView.ItemTemplate>
                            </ListView>
                        </Border>
                    </Expander>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>

And some DataContext:

    public List<object1> List1 { get; set; }
    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;

        List1 = new List<object1>
            {
                new object1("Global Summit", new List<string> { "John Smith", "Maria Garcia", "Hiroshi Tanaka", "Sophie Dubois" }),
                new object1("International Conference", new List<string> { "Elena Petrova", "Rajesh Patel" }),
                new object1("Production", new List<string> { "Luis Hernandez", "Sofia Kim", "Mohammed Ali", "Ol Kapoor", "Anika Schmidt" }),
                new object1("Awareness Training", new List<string> { "Max Mauser"})
            };
        }
    }

    public class object1
    {
        public object1(string title, List<string> persons)
        {
            Title = title;
            Persons = persons;
        }
        public string Title { get; set; }
        public List<string> Persons { get; set; }
    }

This is what i get: enter image description here

As you can see, the Height of each ListViewItems is defined by it's content but I want it to stretch to fit the available space in each row of the WrapPanel and I don't want a fixed Height for all Items, that would just look bad.

This is how i want it to be: enter image description here

I tried changing VerticalAlignment="Stretch" and VerticalContentAlignment="Stretch" for the Content of the ItemTemplate and in the ItemContainerStyle and i tried different margins and alignments in various different places, but nothing worked. It seems like the ListViewItem sets its height to fit the children and i can't find a way to change it.

For Context: In my App, I want the user to be able to drag and drop the persons from one object to another. Which already works, but ist just seems like wasted space and it would feel more natural to the user to just drag and drop a person from one object slightly to the right into the next object, than to drag right and up and having to drop it in the smaller list.


Solution

  • You can insert a Panel or Border which fills the space of ListViewItem and make it hosts ContentPresenter.

    <ListView.ItemContainerStyle>
        <Style TargetType="{x:Type ListViewItem}">
            <Setter Property="Background" Value="LightGray"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ListViewItem}">
                        <Border Background="{TemplateBinding Background}">
                            <ContentPresenter/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ListView.ItemContainerStyle>