Albireo
Albireo

Reputation: 11095

How to place child controls in a certain order when binding a custom control?

What I'm trying to achieve is a custom calendar where I place events.

I've created a basic custom control which simply lists the events:

namespace MyControls
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Web.UI;
    using System.Web.UI.HtmlControls;
    using System.Web.UI.WebControls;

    public class Calendar : CompositeDataBoundControl
    {
        protected override Int32 CreateChildControls(IEnumerable dataSource, Boolean dataBinding)
        {
            Int32 itemCounter = 0;

            if (dataSource != null)
            {
                IEnumerator dataSourceEnumerator = dataSource.GetEnumerator();

                while (dataSourceEnumerator.MoveNext())
                {
                    LinkButton eventLink = new LinkButton();

                    eventLink.Click += new EventHandler(EventLinkClick);

                    HtmlGenericControl eventContainer = new HtmlGenericControl();

                    eventContainer.Controls.Add(eventLink);
                    eventContainer.TagName = "p";

                    this.Controls.Add(eventContainer);

                    if (dataBinding)
                    {
                        CalendarEvent currentEvent = (CalendarEvent) dataSourceEnumerator.Current;

                        eventLink.CommandArgument = String.Concat(currentEvent.Name, "§", currentEvent.Day.ToBinary());
                        eventLink.Text = currentEvent.Name;
                        eventLink.ToolTip = currentEvent.Description;
                    }

                    itemCounter++;
                }
            }

            return itemCounter;
        }

        protected void EventLinkClick(Object sender, EventArgs e)
        {
        }
    }
}

The control works, when I pass it a List<CalendarEvent> it displays every event's LinkButton inside its own <p />, when I click a LinkButton the EventLinkClick method gets called, and after the postback the LinkButtons are still there with their values.

However, I don't need a plain list of the event, I need to place my events inside a calendar, inside the correct day.

I create my calendar like this:

Int32 year = 2011;

Table monthTable = null;

TableRow weekRow = null;

for (DateTime day = new DateTime(year, 1, 1); day.Year == year; day = day.AddDays(1))
{
    if (day.Day == 1)
    {
        HtmlGenericControl monthName = new HtmlGenericControl();

        monthName.InnerText = CultureInfo.CurrentUICulture.DateTimeFormat.GetMonthName(day.Month);
        monthName.TagName = "h2";

        this.Controls.Add(monthName);

        monthTable = new Table();

        TableHeaderRow headerRow = new TableHeaderRow();

        headerRow.TableSection = TableRowSection.TableHeader;

        monthTable.Rows.Add(headerRow);

        for (Int32 i = 0; i < 7; i++)
        {
            TableHeaderCell dayOfWeekCell = new TableHeaderCell();

            dayOfWeekCell.Text = CultureInfo.CurrentUICulture.DateTimeFormat.GetShortestDayName((DayOfWeek) i);

            headerRow.Cells.Add(dayOfWeekCell);
        }

        weekRow = new TableRow();

        weekRow.TableSection = TableRowSection.TableBody;

        for (Int32 i = 0; i < (Int32) day.DayOfWeek; i++)
        {
            weekRow.Cells.Add(new TableCell());
        }
    }

    if (day.DayOfWeek == DayOfWeek.Sunday && day.Day != 1)
    {
        monthTable.Rows.Add(weekRow);

        weekRow = new TableRow();

        weekRow.TableSection = TableRowSection.TableBody;
    }

    TableCell dayCell = new TableCell();

    dayCell.Text = Convert.ToString(day.Day);

    weekRow.Cells.Add(dayCell);

    if (day.Day == DateTime.DaysInMonth(day.Year, day.Month))
    {
        for (Int32 i = (Int32) day.DayOfWeek; i < 6; i++)
        {
            weekRow.Cells.Add(new TableCell());
        }

        monthTable.Rows.Add(weekRow);

        this.Controls.Add(monthTable);
    }
}

which yields to something like this:

An example of my calendar.

Now, how can I integrate the two things?

What I came up with is casting the dataSource parameter to IEnumerable<CalendarEvents> and after the dayCell.Text = Convert.ToString(day.Day); line I get the events of the day from the IEnumerable<CalendarEvents> through LINQ.

However, this breaks on postback because when the control is recreating itself after a postback the dataSource parameter is full of nulls, so I can't fetch the events of the day, so I can't recreate the controls.

I couldn't find anything on the net about this, and I'm completely stuck.

Am I missing (or messing) something? What should I do to achieve what I'm looking for?

Update #1

As StriplingWarrior suggested I tried to save the dataSource in the ViewState, however I failed dramatically.

What I tried is this: at the beginning of the CreateChildControls method I placed

if (dataBinding)
{
    this.ViewState.Add("myDataSource", dataSource);
}

IEnumerable myDataSource = (IEnumerable) this.ViewState["myDataSource"];

and replaced every call to dataSource with myDataSource.

However, when the page post backs this.ViewState["myDataSource"] is null, and I'm back to square one.

I'm starting to regret when I decided to go with a CompositeDataBoundControl... :\

Update #2

I tried to create a new project containing only the custom control, and I rewrote it from scratch, and StriplingWarrior's suggestion worked:

if (dataBinding)
{
    this.ViewState.Add("DataSource", dataSource);
}
else
{
    dataSource = (IEnumerable) this.ViewState["DataSource"];
}

However, I haven't been able to pinpoit what was causing the this.ViewState["DataSource"] in the original solution.

Upvotes: 1

Views: 307

Answers (1)

StriplingWarrior
StriplingWarrior

Reputation: 156524

You're running into ViewState issues. You can either disable viewstate on your control, or write the control in such a way that it saves the information it needs in ViewState, so it doesn't need to rebind to the data source on subsequent postbacks.

Upvotes: 1

Related Questions