0

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!

thatguy
  • 21,059
  • 6
  • 30
  • 40
David Shnayder
  • 333
  • 4
  • 14

1 Answers1

1

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>
thatguy
  • 21,059
  • 6
  • 30
  • 40
  • Ok, so firstly I tried to use the first option first instead of my own, it does display the torrents but the List is basically bound by the size of the of Term. to put into words, I tried to generate a test List that contains 5 Terms, each with 20 Torrents. The in the current window size, the Term displays only 17 Torrents, and if I scroll down, instead of showing the rest of them, it moves to the next term. Any idea why? – David Shnayder Nov 04 '20 at 12:14
  • Also I want to tried your suggested ListView Template, but I need it as a style as it used in multiple windows and files... I tried to modify it to a style myself but being unfamiliar with Listview, I didn't have much success. – David Shnayder Nov 04 '20 at 12:15
  • 1
    @DavidShnayder The scrolling issue is cause by the default _logical scrolling_. That means scrolling by items, not pixels. See this [related post](https://stackoverflow.com/questions/1033841/is-it-possible-to-implement-smooth-scroll-in-a-wpf-listview) or [this one](https://stackoverflow.com/questions/1977929/wpf-listbox-with-a-listbox-ui-virtualization-and-scrolling?noredirect=1&lq=1) on how ro deactivate it. – thatguy Nov 05 '20 at 08:10
  • 1
    @DavidShnayder As for extracting the styles in the second approach, you can just move out the item container style and the cell data templates and share them in a resource dictionary. – thatguy Nov 05 '20 at 09:04
  • Thanks for all your help mate! I really learned a lot. – David Shnayder Nov 05 '20 at 17:12