'Display Data in Group and Horizontal

I am working on a WPF application and I want to design a window that appears like below:

Categories stacked vertically, containing tiles with name and image for products stacked horizontally.

Basically, I want the products to be grouped by category and the products in that category are displayed horizontally first and then vertically.

Which control or way is most efficient to do this?



Solution 1:[1]

Let us assume that you have created data structures for your catgories and products like this:

public class Category
{
   public Category(string name)
   {
      Name = name;
      Products = new ObservableCollection<Product>();
   }

   public string Name { get; }

   public ObservableCollection<Product> Products { get; }
}
public class Product
{
   public Product(string name, ImageSource image)
   {
      Name = name;
      Image = image;
   }

   public string Name { get; }

   public ImageSource Image { get; }
}

Each Category has a name and a collection of Products. The collection is an ObservableCollection<T> so you can add, insert or remove products at runtime and the user interface is automatically notified of the changes and adopts them accordingly. If you want the names or the collection itself to be interchangable, you have to implement the INotifyPropertyChanged interface. If your collections never change, a List<T> or a similar type is sufficient. Each Product has a name and an ImageSource that points to the image to be displayed.

The categories are exposed as a property ofObservableCollection<Category> in your view model.

public ObservableCollection<Category> Categories { get; }

Fill the Categories and Products collections with your data, e.g.:

Categories = new ObservableCollection<Category>
{
   new Category("First"),
   new Category("Second"),
   new Category("Third"),
   new Category("Fourth"),
   // ...other categories.
};

Categories[0].Products.Add(new Product("Product 1.1", new BitmapImage(new Uri(@"pack://application:,,,/Your/Resources/YourImage.png"))));
// ...other products in various categories.

The XAML markup to display the categories and products looks like this:

<Grid Background="Gray">
    <ItemsControl ItemsSource="{Binding Categories}"
                  Margin="0, 20">
        <ItemsControl.ItemTemplate>
            <DataTemplate DataType="local:Category">
                <Border BorderThickness="0, 1, 0, 0"
                        BorderBrush="Black">
                    <StackPanel Margin="10, 0">
                        <TextBlock Text="{Binding Name}"
                                   FontWeight="Bold"
                                   Margin="0, 10"/>
                        <TextBlock x:Name="NoProductTextBlock"
                                   Text="No product"
                                   Visibility="Collapsed"/>
                        <ListBox x:Name="ProductsListBox"
                                 ItemsSource="{Binding Products}"
                                 BorderThickness="0"
                                 Background="Transparent"
                                 Margin="0, 0, 0, 10"
                                 Visibility="Visible">
                            <ListBox.ItemsPanel>
                                <ItemsPanelTemplate>
                                    <WrapPanel/>
                                </ItemsPanelTemplate>
                            </ListBox.ItemsPanel>
                            <ListBox.ItemTemplate>
                                <DataTemplate DataType="local:Product">
                                    <StackPanel>
                                        <Image Source="{Binding Image}"/>
                                        <TextBlock Text="{Binding Name}"
                                                   Margin="0, 10, 0, 0"/>
                                    </StackPanel>
                                </DataTemplate>
                            </ListBox.ItemTemplate>
                        </ListBox>
                    </StackPanel>
                </Border>
                <DataTemplate.Triggers>
                    <DataTrigger Binding="{Binding Products.Count}" Value="0">
                        <Setter TargetName="NoProductTextBlock" Property="Visibility" Value="Visible"/>
                        <Setter TargetName="ProductsListBox" Property="Visibility" Value="Collapsed"/>
                    </DataTrigger>
                </DataTemplate.Triggers>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

What does the markup do to create the user interface you want to achieve?

  • There is an outer Grid that is only there for showing the background for demo purposes.
    • An ItemsControl is the base type for controls showing multiple items of a collection. It does not have any style for states like Mouse Over or Selected, it just displays items. Bind the Categories property to it, so it displays each Category object.
      • A DataTemplate is created to tell the ItemsControl how to display a Category.
        • A Border creates the top horizontal line of each category.
        • The StackPanel contains the elements for the category and the establishes the margin.
          • A TextBlock displays the name of the category by binding Name.
          • A TextBlock displays the placeholder text if there are no products.
          • A ListBox displays all of the Products of a Category. It is bound to the Products property inside Category. In contrast to an ItemsControl, a ListBox has states for MouseOver, Focused and Selected which is what you want to interact with products.
            • As items panel, a WrapPanel is used.

              Positions child elements in sequential position from left to right, breaking content to the next line at the edge of the containing box. Subsequent ordering happens sequentially from top to bottom or from right to left, depending on the value of the Orientation property.

            • Another DataTemplate is used to tell the ListBox how to display a Product. This is simply an Image and a TextBlock stacked vertically.

      • A DataTrigger is used to observe the Count property of the Products collection. If there are no products - Count is zero - we collapse the ListBox and show the placeholder TextBlock instead.

This is what the result looks like with some dummy data:

Categories arranged vertically containing horizontally arranged products with name and image as tile.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 thatguy