views:

388

answers:

5

I've got a repeater looping through a list of objects that are of different types. I'd like to render the objects differently depending on their type. For this I need some kind of control (since I want to avoid using the code-behind) that has a behavior similar to a switch/case statement. Basically it could look like this:

<xxx:TestType Object='<%# Container.DataItem %>'>
    <Case Type="Namespace.ClassX">
        <asp:Label ... />
    </Case>
    <Case Type="Namespace.ClassY">
        <asp:TextBox ... />
    </Case>
    <Default>
        <p>Other</p>
    </Default>
</xxx:TestType>

I've made web controls before, but this is a rather complex one...

  • How do I make it support multiple <Case> elements?
  • Is it possible to implement the <Case Type="..."> elements, or am I limited to attribute-less elements?

I'm guessing I have to make a type for the <Case> elements and somehow specifying it for the main web control?

I'd be grateful for any pointers, links to tutorials, etc!

Alternative

Alternatively, suggest a nicer way of rendering different HTML/ASP.NET controls based on the type of the currently bound object. The first method that popped into my head was this, which I consider (very) ugly:

<asp:PlaceHolder Visible='<%# CheckType(Container, typeof(ClassX)) %>' runat="server">
...
</asp:PlaceHolder>
<asp:PlaceHolder Visible='<%# CheckType(Container, typeof(ClassY)) %>' runat="server">
...
</asp:PlaceHolder>
A: 

Why don't you ditch the server control and use the C# switch statement. All you need is to wrap it in the code tags.

Another approach is a iteration (for loop). Create an interface with a type property and a render method. Then iterate over the interfaces until you've found the correct type.

Chuck Conway
Yeah that's the other option... My idea was to make it more... ASP.NET-ish since I'm working with templates after all...
Blixt
Also, since I need to check the data bound object, I can't just use the `switch` statement in code tags, since the `Container` member is only available in data-binding tags (`<%# ... %>`)
Blixt
It seems like a lot of work... just to avoid the codebehind and embedding code into your HTML. On the bright-side you will learn tons!
Chuck Conway
A: 

The MultiView control is the closest thing out-of-the-box in ASP.NET to a C# switch statement.

Joe Chung
`MultiView` turned out to be the solution with the best functionality/work required ratio in my case.
Blixt
A: 

I had a similar requirement a while ago and I did something similar to:

[ParseChildren(true, "Templates")]
public class TypedRepeater : CompositeDataBoundControl
{
    [
    PersistenceMode(PersistenceMode.InnerProperty),
    Browsable(false),
    MergableProperty(false)
    ]
    public TypedTemplateCollection Templates
    {
        get;
    }

protected TypedTemplate GetTemplate(object dataItem) { if (dataItem == null) { return null; } foreach (TypedTemplate template in Templates) { Type itemType = dataItem.GetType(); if (dataItem.IsAssignableFrom(template.Type)) { return template; } } return null; }

    protected TypedTemplateRepeaterItem CreateItem(int index, object dataItem, bool dataBinding)
    {
        TypedTemplateRepeaterItem repeaterItem = new TypedTemplateRepeaterItem();
        if ((!dataBinding) && (ViewState[string.Format("TemplateIxc_{0}", index)] is int))
        {
            int _template = (int)ViewState[string.Format("TemplateIxc_{0}", index)];
            if ((_template >= 0) && (_template < Templates.Count) && (Templates[_template].ItemTemplate != null))
            {
                Templates[_template].ItemTemplate.InstantiateIn(repeaterItem);
            }
            else
            {
                DefaultTemplate.InstantiateIn(repeaterItem);
            }
        }
        else if (dataBinding)
        {
            TypedTemplate template = GetTemplate(dataItem);
            ITemplate itemTemplate = DefaultTemplate;
            if (template != null)
            {
                itemTemplate = template.ItemTemplate;
                ViewState[string.Format("TemplateIxc_{0}", index)] = Templates.IndexOf(template);
            }
            else
            {
                ViewState[string.Format("TemplateIxc_{0}", index)] = -1;
            }

            repeaterItem.DataItem = dataItem;
            repeaterItem.DataItemIndex =
                repeaterItem.DisplayIndex = index;
            itemTemplate.InstantiateIn(repeaterItem);
            repeaterItem.DataBind();
            repeaterItem.DataItem = null;
        }
        return repeaterItem;
    }

    protected override int CreateChildControls(IEnumerable dataSource, bool dataBinding)
    {
        int count = 0;
        if (dataSource != null)
        {
            foreach (object dataItem in dataSource)
            {
                TypedTemplateRepeaterItem repeaterItem = CreateItem(count, dataItem, dataBinding);      
                Controls.Add(repeaterItem);
                count++;
            }
        }
        return count;
    }
}

where TypedTemplateCollection is a StateManagedCollection of a TypedTemplate class:

[ParseChildren(true, "ItemTemplate")]
public class TypedTemplate
{

    public Type Type
    {
        get { return Type.GetType(TypeName); }
    }

    [
    PersistenceMode(PersistenceMode.Attribute),
    Browsable(true),
    DefaultValue("")
    ]
    public string TypeName
    {
        get;
        set;
    }

    [
    PersistenceMode(PersistenceMode.InnerProperty),
    Browsable(true),
    DefaultValue(typeof(ITemplate), null),
    TemplateContainer(typeof(TypedTemplateRepeaterItem))
    ]
    public ITemplate ItemTemplate
    {
        get;
        set;
    }
}

and TypedTemplateRepeaterItem is:

public class TypedTemplateRepeaterItem : WebControl, INamingContainer, IDataItemContainer
{
    #region IDataItemContainer Members

    public object DataItem
    {
        get;
        set;
    }

    public int DataItemIndex
    {
        get;
        set;
    }

    public int DisplayIndex
    {
        get;
        set;
    }

    #endregion
}
veggerby
A: 

Look at implementing ITemplate interface

  public class Case        
    {

        [PersistenceMode(PersistenceMode.Attribute)]
        public string Type { get; set; }

        [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
        public ITemplate Template { get; set; }

    }

    public class DeclarativeCase
        : CompositeControl
    {
        [PersistenceMode(PersistenceMode.InnerProperty)]
        public List<Case> Cases { get; set; }

        [PersistenceMode(PersistenceMode.InnerProperty)]
        public ITemplate Default { get; set; }
    }

<xxx:DeclarativeCase runat="server" ID="test">
  <Cases>
   <xxx:Case Type="Namespace.TypeName">
    <Template>
     <asp:Label ID="Label1" runat="server"></asp:Label>
    </Template>
   </xxx:Case>
  </Cases>

<Default>
 <asp:Label ID="Label2" runat="server"></asp:Label>
</Default>

</xxx:DeclarativeCase>
Jan Remunda
A: 

The MSDN site has a simple clear example of templating and it certainly seems like the way to go in your case.

pthalacker