David Shnayder
David Shnayder

Reputation: 333

Listbox style with items that contains lists

I am building a webscaper to scrape torrents, backend is already done and now I am kind of stuck on the UI(using WPF). I want to display the torrents on a ListBox but corresponding to the query (I call it term as in search term).

The data passed to the ListBox is:

List<SearchTermData>

This is the structure:

public class SearchTermData {
   public string SearchTerm { get; set; }
   public List<Torrent> Torrents { get; set; } = new List<Torrent>();
}

Torrent is this:

public class Torrent {
   public string Name { get; set; }
   public string Size { get; set; }
}

I generally want the ListBox to display a Grid. Column 1 will be the "SearchTerm" and column 2 will be a StackPanel of "Torrents" (List<Torrent>).

It displays the "SearchTermData" correctly, with the SearchTerm name and the general structure, but it fails to display the StackPanel of the Torrents.

This is my XAML style:

<Style x:Key="TorrentListbox" TargetType="ListBox">
   <Setter Property="UseLayoutRounding" Value="True" />
   <Setter Property="BorderThickness" Value="0" />
   <Setter Property="BorderBrush" Value="{StaticResource Black}"/>
   <Setter Property="Background" Value="Transparent" />
   <Setter Property="FontFamily" Value="{StaticResource DefaultFont}" />
   <Setter Property="FontWeight" Value="Regular" />
   <Setter Property="HorizontalContentAlignment" Value="Left" />
   <Setter Property="SelectionMode" Value="Multiple" />
   <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden" />
   <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
   <Setter Property="ScrollViewer.CanContentScroll" Value="True" />
   <Setter Property="ItemContainerStyle" Value="{DynamicResource TermItem}" />
</Style>

<Style x:Key="TermItem" TargetType="ListBoxItem">
   <Setter Property="SnapsToDevicePixels" Value="True" />
   <Setter Property="OverridesDefaultStyle" Value="True" />
   <Setter Property="BorderThickness" Value="2"/>
   <Setter Property="BorderBrush" Value="{StaticResource DSGray}"/>
   <Setter Property="Foreground" Value="{StaticResource Black}" />
   <Setter Property="FontFamily" Value="{StaticResource DefaultFont}" />
   <Setter Property="FontWeight" Value="Regular" />
   <Setter Property="FontSize" Value="16" />
   <Setter Property="Margin" Value="0,0,0,4"/>
   <Setter Property="ScrollViewer.CanContentScroll" Value="True" />
   <Setter Property="UseLayoutRounding" Value="True" />
   <Setter Property="Template">
      <Setter.Value>
         <ControlTemplate TargetType="ListBoxItem">
            <Border x:Name="Border" BorderBrush="{TemplateBinding BorderBrush}" CornerRadius="0" UseLayoutRounding="{TemplateBinding UseLayoutRounding}" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels="true">
               <Grid Background="{TemplateBinding Background}" HorizontalAlignment="Stretch" UseLayoutRounding="{TemplateBinding UseLayoutRounding}" Visibility="Visible">
                  <Grid.ColumnDefinitions>
                     <ColumnDefinition Width="Auto"/>
                     <ColumnDefinition Width="*"/>
                  </Grid.ColumnDefinitions>
                  <Grid.RowDefinitions>
                     <RowDefinition Height="*"/>
                  </Grid.RowDefinitions>
                  <Label Content="{Binding Path=SearchTerm}" BorderThickness="0,0,2,0" BorderBrush="{StaticResource Black}" Style="{StaticResource KeyLabel}" Foreground="{StaticResource DSGreen}" FontWeight="Bold" VerticalContentAlignment="Top" Grid.Row="0" Grid.Column="0"/>
                  <StackPanel Grid.Row="0" Grid.Column="1">
                     <ItemsControl ItemsSource="{Binding Path=Torrents}">
                        <ItemsControl.ItemTemplate>
                           <DataTemplate>
                              <Border BorderThickness="1" BorderBrush="{StaticResource DSGreen}">
                                 <ItemsControl ItemsSource="{Binding Torrent}">
                                    <ItemsControl.ItemTemplate>
                                       <DataTemplate>
                                          <Grid HorizontalAlignment="Stretch" UseLayoutRounding="{TemplateBinding UseLayoutRounding}" Visibility="Visible">
                                             <Grid.ColumnDefinitions>
                                                <ColumnDefinition Width="*"/>
                                                <ColumnDefinition Width="Auto"/>
                                                <ColumnDefinition Width="Auto"/>
                                             </Grid.ColumnDefinitions>
                                             <Grid.RowDefinitions>
                                                <RowDefinition Height="*"/>
                                             </Grid.RowDefinitions>
                                             <Label Content="{Binding Path=Name}" Style="{StaticResource PropertyLabel}" Grid.Row="0" Grid.Column="0"/>
                                             <Label Content="Size:" Style="{StaticResource KeyLabel}" Grid.Row="0" Grid.Column="1"/>
                                             <Label Content="{Binding Path=Size}" Style="{StaticResource PropertyLabel}" Grid.Row="0" Grid.Column="2"/>
                                          </Grid>
                                       </DataTemplate>
                                    </ItemsControl.ItemTemplate>
                                 </ItemsControl>
                              </Border>
                           </DataTemplate>
                        </ItemsControl.ItemTemplate>
                     </ItemsControl>
                  </StackPanel>
               </Grid>
            </Border>
         </ControlTemplate>
      </Setter.Value>
   </Setter>
</Style>

If anyone can help me figure out the issue I would appreciate it a lot!

Upvotes: 0

Views: 397

Answers (1)

thatguy
thatguy

Reputation: 22119

In your items template for Torrent, there is a nested ItemsControl that is not needed and that is binding to a property Torrent on an item of type Torrent, which does not exist.

<ItemsControl ItemsSource="{Binding Torrent}">

The control template below should work. I removed the redundant ItemsControl and the StackPanel as well, because it only contains a single control, which is why you do not need it.

<ControlTemplate TargetType="ListBoxItem">
   <Border x:Name="Border" BorderBrush="{TemplateBinding BorderBrush}" CornerRadius="0" UseLayoutRounding="{TemplateBinding UseLayoutRounding}" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels="true">
      <Grid Background="{TemplateBinding Background}" HorizontalAlignment="Stretch" UseLayoutRounding="{TemplateBinding UseLayoutRounding}" Visibility="Visible">
         <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
         </Grid.ColumnDefinitions>
         <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
         </Grid.RowDefinitions>
         <Label Content="{Binding Path=SearchTerm}" BorderThickness="0,0,2,0" BorderBrush="{StaticResource Black}" Style="{StaticResource KeyLabel}" Foreground="{StaticResource DSGreen}" FontWeight="Bold" VerticalContentAlignment="Top" Grid.Row="0" Grid.Column="0"/>
         <ItemsControl Grid.Row="0" Grid.Column="1" ItemsSource="{Binding Path=Torrents}">
            <ItemsControl.ItemTemplate>
               <DataTemplate>
                  <Border BorderThickness="1" BorderBrush="{StaticResource DSGreen}">
                     <Grid HorizontalAlignment="Stretch" UseLayoutRounding="{TemplateBinding UseLayoutRounding}" Visibility="Visible">
                        <Grid.ColumnDefinitions>
                           <ColumnDefinition Width="*"/>
                           <ColumnDefinition Width="Auto"/>
                           <ColumnDefinition Width="Auto"/>
                        </Grid.ColumnDefinitions>
                        <Grid.RowDefinitions>
                           <RowDefinition Height="*"/>
                        </Grid.RowDefinitions>
                        <Label Content="{Binding Path=Name}" Style="{StaticResource PropertyLabel}" Grid.Row="0" Grid.Column="0"/>
                        <Label Content="Size:" Style="{StaticResource KeyLabel}" Grid.Row="0" Grid.Column="1"/>
                        <Label Content="{Binding Path=Size}" Style="{StaticResource PropertyLabel}" Grid.Row="0" Grid.Column="2"/>
                     </Grid>
                  </Border>
               </DataTemplate>
            </ItemsControl.ItemTemplate>
         </ItemsControl>
      </Grid>
   </Border>
</ControlTemplate>

An Alternative Using ListView with GridView

Since you want to display two columns, you could alternatively use a ListView in combination with a GridView. This way, you would actually have real columns.

<ListView ItemsSource="{Binding SearchTerms}">
   <ListView.ItemContainerStyle>
      <Style TargetType="ListViewItem">
         <Setter Property="SnapsToDevicePixels" Value="True" />
         <Setter Property="OverridesDefaultStyle" Value="True" />
         <Setter Property="BorderThickness" Value="2"/>
         <Setter Property="BorderBrush" Value="{StaticResource DSGray}"/>
         <Setter Property="Foreground" Value="{StaticResource Black}" />
         <Setter Property="FontFamily" Value="{StaticResource DefaultFont}" />
         <Setter Property="FontWeight" Value="Regular" />
         <Setter Property="FontSize" Value="16" />
         <Setter Property="Margin" Value="0,0,0,4"/>
         <Setter Property="ScrollViewer.CanContentScroll" Value="True" />
         <Setter Property="UseLayoutRounding" Value="True" /> 
         <Setter Property="VerticalContentAlignment" Value="Stretch" />
         <Setter Property="HorizontalContentAlignment" Value="Stretch" />
         <Setter Property="Template">
            <Setter.Value>
               <ControlTemplate TargetType="{x:Type ListViewItem}">
                  <Border x:Name="Border" BorderBrush="{TemplateBinding BorderBrush}" CornerRadius="0" UseLayoutRounding="{TemplateBinding UseLayoutRounding}" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels="true">
                     <GridViewRowPresenter/>
                  </Border>
               </ControlTemplate>
            </Setter.Value>
         </Setter>
      </Style>
   </ListView.ItemContainerStyle>
   <ListView.View>
      <GridView>
         <GridViewColumn Header="Search Term">
            <GridViewColumn.CellTemplate>
               <DataTemplate>
                  <Label Content="{Binding Path=SearchTerm}" BorderThickness="0,0,2,0" BorderBrush="Black" Foreground="Green" FontWeight="Bold"/>
               </DataTemplate>
            </GridViewColumn.CellTemplate>
         </GridViewColumn>
         <GridViewColumn Header="Torrents">
            <GridViewColumn.CellTemplate>
               <DataTemplate>
                  <ItemsControl ItemsSource="{Binding Torrents}">
                     <ItemsControl.ItemTemplate>
                        <DataTemplate>
                           <Border BorderThickness="1" BorderBrush="Green">
                              <Grid HorizontalAlignment="Stretch" UseLayoutRounding="{TemplateBinding UseLayoutRounding}" Visibility="Visible">
                                 <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="*"/>
                                    <ColumnDefinition Width="Auto"/>
                                    <ColumnDefinition Width="Auto"/>
                                 </Grid.ColumnDefinitions>
                                 <Grid.RowDefinitions>
                                    <RowDefinition Height="*"/>
                                 </Grid.RowDefinitions>
                                 <Label Content="{Binding Path=Name}" Grid.Row="0" Grid.Column="0"/>
                                 <Label Content="Size:" Grid.Row="0" Grid.Column="1"/>
                                 <Label Content="{Binding Path=Size}" Grid.Row="0" Grid.Column="2"/>
                              </Grid>
                           </Border>
                        </DataTemplate>
                     </ItemsControl.ItemTemplate>
                  </ItemsControl>
               </DataTemplate>
            </GridViewColumn.CellTemplate>
         </GridViewColumn>
      </GridView>
   </ListView.View>
</ListView>

Upvotes: 1

Related Questions