Reputation: 11095
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 LinkButton
s 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:
.
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?
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
... :\
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
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