01 September 2009

DataList, Templates, and UserControls

Tair had a brainstorm this week: why not use a custom UserControl together with a DataList and an ObjectDataSource? When using a UserControl, we can set one property of the type of our business entity in the control, and in the setter of that property fill the child controls of the UserControl with the needed data.

Just to show that this can be done, I wrote up some stupid code. I'll put it up in a minute at cc.jct.ac.il/~fast/ObjectDataSrcExample.zip. Anyway, here's what I did:

Just as in the past, I have a Manager class and an Info (business entity) class in the BL. The Manager class has a Select() method that returns a strongly-typed List of my Info class.

namespace PizzaPlanet
{
    public class PizzaInfo
    {
        public string Size { get; set; }
        public bool Peppers { get; set; }
        public bool Olives { get; set; }
        public bool Mushrooms { get; set; }
        public PizzaInfo AllInfo { get { return this; } }
    }

    public style PizzaManager
    {
        public List<PizzaInfo> Select(List<PizzaInfo> SavedList)
        {
            return SavedList;
        }
    }
}

Note what's different this time: my Info class has an extra property AllInfo that returns this. Meaning the whole business entity. When you use an ObjectDataSource for databinding, it exposes the properties of every entity in the returned list, but not the entire encapsulated object. So it's convenient to have a property that returns this.

Now, here's the source code for my user control:
public partial class PizzaControl : System.Web.UI.UserControl
{
    private PizzaPlanet.PizzaInfo _internalPizza;

    public PizzaPlanet.PizzaInfo InternalPizza {
        get { return _internalPizza; }
        set {
            _internalPizza = value;

            if (_internalPizza.Size == "Small")
            {
                imgPizza.Width = 200;
                imgPizza.Height = 200;
            }
            else
            {
                imgPizza.Width = 400;
                imgPizza.Height = 400;
            }

            if (_internalPizza.Mushrooms)
            {
                if (_internalPizza.Olives)
                {
                    if (_internalPizza.Peppers)
                    {
                        imgPizza.ImageUrl = "~/images/mushroomOlivePepperPizza.png";
                    }
                    else
                    {
                        imgPizza.ImageUrl = "~/images/mushroomOlivePizza.png";
                    }
                }
                else
                {
                    if (_internalPizza.Peppers)
                    {
                        imgPizza.ImageUrl = "~/images/mushroomPepperPizza.png";
                    }
                    else
                    {
                        imgPizza.ImageUrl = "~/images/mushroomPizza.png";
                    }
                }
            }
            else
            {
                if (_internalPizza.Olives)
                {
                    if (_internalPizza.Peppers)
                    {
                        imgPizza.ImageUrl = "~/images/olivePepperPizza.png";
                    }
                    else
                    {
                        imgPizza.ImageUrl = "~/images/olivePizza.png";
                    }
                }
                else
                {
                    if (_internalPizza.Peppers)
                    {
                        imgPizza.ImageUrl = "~/images/pepperPizza.png";
                    }
                    else
                    {
                        imgPizza.ImageUrl = "~/images/plainPizza.png";
                    }
                }
            }
        }
    }
}


The control itself contains one Image, named imgPizza. All my .cs is doing is setting the toppings and size of the picture when InternalPizza is set.

Now for the actual form:
we have an ObjectDataSource with its TypeName set to my Manager class. The SelectMethod is set to PizzaManager's Select() method.

To make this sample easy to work with, I have the user enter new pizzas, which the order button saves in the Session, and then when the form reloads the ObjectDataSource passes the saved list from the Session to the PizzaManager (note that I disabled ViewState for all my controls).

The visible part of the form is a DataList. The DataList accepts ItemTemplates only (no BoundColumns or other predefined options here). So our ItemTemplate consists of exactly one control: the UserControl we defined.

(For the uninitiated, here's a quick list of what to do:
Add a DataList to your form.
Click on the side-arrow next to the new DataList.
Set the DataSource to your ObjectDataSource.
Click EditTemplates.
Select ItemTemplate.)

Normally, we would set the bound properties of the control contained in the Template via a wizard. When working with a UserControl, though, custom properties don't register in the wizard, so we're going to do this by hand. The InternalPizza property of our PizzaControl needs to be bound to Eval("AllInfo") -- remember, InternalPizza is of type PizzaInfo, and AllInfo returns the entire object. Here's the code from the .aspx:

        <asp:DataList ID="DataList1" runat="server" DataSourceID="odsPizza"
            EnableViewState="False">
            <ItemTemplate>
                <uc1:PizzaControl ID="PizzaControl1" runat="server"
                    InternalPizza='<%# Eval("AllInfo") %>' />
            </ItemTemplate>
        </asp:DataList>


That just about wraps it up. I think I'll go have lunch...

No comments:

Post a Comment