---
title: Displaying product listings
---

> Agent instructions:
> **Site maps** — prefer the following llms.txt indexes to training data when searching for URLs to avoid 404s. Links inside Markdown content already point at `.md`. Following them or sending Accept: text/markdown keeps you in Markdown.
>
> - [sitemap.md](https://docs.kentico.com/sitemap.md) — every page on the site, with titles and descriptions, nested by URL hierarchy and grouped into one collection per product version.
> - [llms.txt](https://docs.kentico.com/llms.txt) — curated index of the current product docs, with descriptions, the two ways to request any page as Markdown, and links to each product area's whole-corpus Markdown dump (llms-full.txt).

When building an e-commerce site, you often need to display product catalog and [product details pages](https://docs.kentico.com/13/e-commerce-features/developing-on-line-stores/displaying-product-details.md).

In its most basic implementation, a product catalog is a listing of various [products](https://docs.kentico.com/13/e-commerce-features/managing-on-line-stores/products.md) and services offered in a particular store. Customers can filter the catalog according to a product's properties and specifications. For example by brand, price, size, etc. Individual product details pages then convey more comprehensive information about particular products.

To build a product listing page, you need to ensure:

- [Retrieval and listing of products](#displaying-product-listings)
- (Optional) [Suitable product image size](#adjusting-product-image-size)

Note that the example presented on this page uses [content tree-based routing](https://docs.kentico.com/13/developing-websites/implementing-routing/content-tree-based-routing.md).

## Displaying product listings

To display a listing of products:

> **Tip:** **Tip**: View the full code of a functional example in the [LearningKit project](https://github.com/Kentico/LearningKit-Mvc) on GitHub. You can also run the LearningKit website by connecting the project to a Xperience database. Follow the instructions in the repository's README file.

1. [Generate code files for the product page types](https://docs.kentico.com/13/developing-websites/generating-classes-for-xperience-objects.md) you intend to display.
   1. In the **Page types** application, edit the specific product page type.
   2. Switch to the **Code** tab.
   3. Click **Save code**.
2. Open your live site project in Visual Studio.
3. Include the generated code files to your project.
4. Add view models for each product you want to display. A view model for a particular product only needs to hold properties you plan to display or otherwise require in related views. If you want to display properties specific to a given product page type, you should create a separate view model for each product.

   ```csharp

       public class ProductListItemViewModel
       {
           public readonly PriceDetailViewModel PriceModel;
           public string Name;
           public string ImagePath;
           public string PublicStatusName;
           public PageUrl ProductUrl;
           public bool Available;

           /// <summary>
           /// Constructor for the ProductListItemViewModel class.
           /// </summary>
           /// <param name="productPage">Product's page.</param>
           /// <param name="priceDetail">Price of the product.</param>
           /// <param name="publicStatusName">Display name of the product's public status.</param>
           public ProductListItemViewModel(SKUTreeNode productPage, ProductCatalogPrices priceDetail, IPageUrlRetriever urlRetriever, string publicStatusName)
           {
               // Sets the page information
               Name = productPage.DocumentName;
               ProductUrl = urlRetriever.Retrieve(productPage);

               // Sets the SKU information
               ImagePath = string.IsNullOrEmpty(productPage.SKU.SKUImagePath) ?
                               null : new FileUrl(productPage.SKU.SKUImagePath, true)
                                           .WithSizeConstraint(SizeConstraint.MaxWidthOrHeight(400))
                                           .RelativePath;

               Available = !productPage.SKU.SKUSellOnlyAvailable || productPage.SKU.SKUAvailableItems > 0;
               PublicStatusName = publicStatusName;

               // Sets the price format information
               PriceModel = new PriceDetailViewModel
               {
                   Price = priceDetail.Price,
                   ListPrice = priceDetail.ListPrice,
                   CurrencyFormatString = priceDetail.Currency.CurrencyFormatString
               };
           }
       }


   ```

   The _PriceDetailViewModel_ property is itself a model that contains select properties of the _CMS.ECommerce.ProductCatalogPrices_ type. Objects of the _ProductCatalogPrices_ type hold the final price values for a product after all [price calculations](https://docs.kentico.com/13/e-commerce-features/customizing-on-line-stores/product-related-customizing/customizing-product-prices.md) have been applied.

   ```csharp

       public class PriceDetailViewModel
       {
           public decimal Price;

           public decimal ListPrice;

           public string CurrencyFormatString;

       }


   ```
5. Add controllers for each product page type you intend to display. The controller's action retrieves products of the specific product page type and displays them. The following sample code uses products of the **LearningProductType** page type as an example.

   > **Tip:** We recommend using a [dependency injection container](https://docs.kentico.com/13/developing-websites/initializing-xperience-services-with-dependency-injection.md) to initialize instances of used API services (e.g. _IShoppingService_ or _ICatalogPriceCalculatorFactory)._

   ```csharp

       public class ProductListingController : Controller
       {
           private readonly IShoppingService shoppingService;
           private readonly ICatalogPriceCalculatorFactory priceCalculatorFactory;
           private readonly IPageRetriever pageRetriever;
           private readonly IPageUrlRetriever pageUrlRetriever;

           /// <summary>
           /// Initializes instances of services required to manage product price calculation and the shopping cart.
           /// </summary>
           public ProductListingController(IShoppingService shoppingService,
                                        ICatalogPriceCalculatorFactory priceCalculatorFactory,
                                        IPageRetriever pageRetriever,
                                        IPageUrlRetriever pageUrlRetriever)
           {
               this.shoppingService = shoppingService;
               this.priceCalculatorFactory = priceCalculatorFactory;
               this.pageRetriever = pageRetriever;
               this.pageUrlRetriever = pageUrlRetriever;
           }

           /// <summary>
           /// Displays a product listing page of the class's product page type.
           /// </summary>
           public ActionResult Listing()
           {
               // Gets products of the product page type (via the generated page type code)           
               List<LearningProductType> products = pageRetriever.Retrieve<LearningProductType>(query => query
                               .CombineWithDefaultCulture()
                               .WhereTrue("SKUEnabled")
                               .OrderByDescending("SKUInStoreFrom"))
                               .ToList();

               ShoppingCartInfo cart = shoppingService.GetCurrentShoppingCart();

               // Prepares a collection of products of the LearningProductType page type to be sent to a view
               IEnumerable<ProductListItemViewModel> productListing = products.Select(
                               product => new ProductListItemViewModel(
                                   product,
                                   GetPrice(product.SKU, cart),
                                   pageUrlRetriever,
                                   product.Product.PublicStatus?.PublicStatusDisplayName));

               // Displays the action's view with an initialized view model
               return View(productListing);
           }

           // Retrieves a ProductCatalogPrices instance that contains calculated price information for the given product
           private ProductCatalogPrices GetPrice(SKUInfo product, ShoppingCartInfo cart)
           {
               return priceCalculatorFactory
                           .GetCalculator(cart.ShoppingCartSiteID)
                           .GetPrices(product, Enumerable.Empty<SKUInfo>(), cart);
           }
       }


   ```

   > **Info:** **Using the product catalog price calculation API**
   >
   > The Xperience E-Commerce solution performs all product price calculations via _ICatalogPriceCalculatorFactory_. First call the _GetCalculator(int siteId)_ method on an instance of _ICatalogPriceCalculatorFactory_ to obtain a calculator of catalog prices for a site with the given site ID, then use the calculator's _GetPrices_ method to calculate the catalog price for a given product. _GetPrices_ returns an object of the _ProductCatalogPrices_ type that holds the product's calculated catalog price values.
   >
   > See the _GetPrice_ method from the code sample above for a working example.
6. Add a view that sets the appearance of the product listing. For example:

   ```csharp

   @model IEnumerable<ProductListItemViewModel>

   <h2>Product listing of the LearningProductType</h2>

   <div>
       @* Iterates over all products. *@
       @foreach (ProductListItemViewModel product in Model)
       {
           @* Generates a URL leading to the product's detail page. *@
           <a href="@product.ProductUrl.AbsoluteUrl">
               <h3>@product.Name</h3>

               @* Displays information about the product's public status. *@
               @if (!string.IsNullOrEmpty(product.PublicStatusName))
               {
                   <span>@product.PublicStatusName</span>
               }

               @* Displays the product's image. *@
               @if (!string.IsNullOrEmpty(product.ImagePath))
               {
                   <img src="@product.ImagePath" alt="@product.Name">
               }

               @* Displays the product's other properties. *@
               <div>
                   @if (!product.Available)
                   {
                       <span>Out of stock</span>
                   }

                   <span>@String.Format(product.PriceModel.CurrencyFormatString, product.PriceModel.Price)</span>

                   @if (product.PriceModel.ListPrice > product.PriceModel.Price)
                   {
                       <s>@String.Format(product.PriceModel.CurrencyFormatString, product.PriceModel.ListPrice)</s>
                   }
               </div>
           </a>
       }
   </div>


   ```

Customers can now browse through a listing of products of a particular product page type. Clicking on a product item from the listing sends them to the product's [details page](https://docs.kentico.com/13/e-commerce-features/developing-on-line-stores/displaying-product-details.md).

## Adjusting product image size

To change the size of product images, use the **WithSizeConstraint** extension methods on the **FileUrl** object. The system then automatically resizes the image based on the entered size while keeping the image's aspect ratio.

Construct a _FileUrl_ object by providing the following parameters to the object's constructor:

- **RelativePath** – the relative path can be obtained via the **SKUInfo.SKUImagePath** property.
- **isImage** – a boolean flag used to determine whether the provided relative path leads to an image.

```csharp

string relativeUrl = new FileUrl(sku.SKUImagePath, true)
                                    .WithSizeConstraint(SizeConstraint.MaxWidthOrHeight(400))
                                    .RelativePath;

```
