Dynamic ListView LayoutTemplate

There are times when you want to let the user change layout dynamically. You can use css to do this but lets look at what the ListView control offers. To get started with the ListView you need a LayoutTemplate and ItemTemplate.

<asp:ListView runat="server" ID="listView">       

    <LayoutTemplate>

        <asp:PlaceHolder runat="server" ID="itemPlaceholder"></asp:PlaceHolder>

    </LayoutTemplate>

    <ItemTemplate>

        <%# Eval("CategoryName") %>

    </ItemTemplate>

</asp:ListView>

The ListView replaces the control with ID="itemPlaceholder" with the zero or more instances of the Selected/Alternating/ItemTemplate.

There is a method on TemplateControl (LoadTemplate) which allows users to dynamically load a user control as a ITemplate. Lets use this to load our LayoutTemplate from a user control.

User Control:


<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="FlowLayout.ascx.cs" Inherits="ListViewLayouts.FlowLayout" %>

 

<div id="flow">

    <asp:PlaceHolder runat="server" ID="itemPlaceholder"></asp:PlaceHolder>

</div>

Code Behind:

protected void Page_Init(object sender, EventArgs e) {

    listView.LayoutTemplate = LoadTemplate("~/ListViewLayouts/FlowLayout.ascx");

}

When we run the page we get the following exception:

An item placeholder must be specified on ListView 'listView'. Specify an item placeholder by setting a control's ID property to "itemPlaceholder". The item placeholder control must also specify runat="server".

The problem here is that user controls are naming containers and when the ListView internally tries to find the control with ID="itemPlaceholder" it fails. So what is the id of the itemPlaceholder? We'll we could start guessing that it might be something like ctl001$itemPlaceholder, but that doesn't seem like a good solution. Instead we can create our own template that will allow us to specify the ID of the user control so that the itemPlaceholderID is more predictable.

public class CustomTemplate : ITemplate {

    private string _virtualPath;

    private string _controlID;

 

    public CustomTemplate(string virtualPath, string controlID) {

        _virtualPath = virtualPath;

        _controlID = controlID;

    }

 

    public void InstantiateIn(Control container) {           

        Control control = (Control)BuildManager.CreateInstanceFromVirtualPath(_virtualPath, typeof(Control));

        control.ID = _controlID;

        container.Controls.Add(control);

    }

}


This template gives us the opportunity to specify a controlID for the user control we are going to load. Now we instantiate a new CustomTemplate and specify the control ID as well as path to the user control.

protected void Page_Load(object sender, EventArgs e) {

    listView.LayoutTemplate = new CustomTemplate("~/ListViewLayouts/FlowLayout.ascx", "flowLayout");           

}

Don't forget to set the ItemPlaceHolderID proprety on the ListView.

<asp:ListView runat="server" ID="listView" ItemPlaceholderID="flowLayout$itemPlaceholder">

        <ItemTemplate>

            <%# Eval("CategoryName") %>

        </ItemTemplate>

</asp:ListView>

Now we can load the LayoutTemplate at runtime.

7 Comments

  • Thanks for such an excellent post.
    I have an issue, the page is initially loading well but when the page is posted back(using a dropdownlist having items like flowlayout, thumbnail layout etc) its giving the same exception you mentioned in the post.

  • Do you also change the ItemPlaceholderID property? Notice in this case the ID is flowLayout$ItemPlaceholder. If your changing the layout dynamically you'll also need to set the ItemPlaceholderID.

  • Won't this code have a view state issue? I see you are creating your template in Page_Load and not Page_Init.
    I have found you can sync the template like this. I can send code if you want. Email, OSaienni@assima.net.
    Page_Init()
    -&gt;Create LayoutTemplate control creator object with a reference to the owning page that owns the listview object.
    (This object is of type ITemplate and this object will synchronise the controls needed for list view but will have added advantage of keeping their view states)
    -&gt;ASP does its magic and loads up the viewstate of the owning page object and calls InstantiateIn by this stage page object has valid synchronisation values.
    In InstantiateIn sync to the current sync needed. You can do this writing code to create objects explicitly(I'm old school) or using the loading template method.
    By the time this runs, your list view has been created.
    You can do the same for ItemTemplate. So this means for same page your listview can have many different view types.
    I've never had to do ItemPlaceholderID="flowLayout$itemPlaceholder" as the itemPlaceholder is created only once.
    I've seen another method too, whereby someone creates the controls, listens to the event, deletes the controls and recreate it. This solution solves that issue too.

  • Thank you very much. &nbsp;I was banging my head against a brick wall until reading this article.
    Well laid out and easy to understand too. &nbsp;Kudos.

  • Hi
    very Good and usefull Sample

  • still i cant use in user control .. how can i do that .... "itemPlaceholder" in user control is creating problem

  • I want to know &nbsp;about the dynamic creation of listview....

Comments have been disabled for this content.