Create Dynamic Buttons in an ASP.NET Calendar

Posted by: Aaron Goldenthal 3/31/2009 2:56 PM

As you get more familiar with the ASP.NET calendar, eventually you start tweaking it: adding your own styles, using the DayRender event to customize the content of each day, etc.  At some point you may try to dynamically create a button during DayRender, at which point you see the button render on the client, but when it’s clicked, no event fires.  This article will give you a solution to work around this problem.

Why Adding Buttons During DayRender Fails

Some people who start using more advanced techniques to tweak the Calendar assume initially that it’s similar to the other ASP.NET Data Controls (e.g. Repeater, GridView, ListView) so you can use DayRender on a Calendar like you would use ItemDataBound in a Repeater, or RowDataBound in a GridView.  That is not the case.  The Calendar’s DayRender event is exactly what is says it is - it’s an event fired during the render process, just before the cell for the given day is rendered.  Because this event is fired so late in the page life cycle, you cannot add controls that fire events.  From the Calendar documentation:

Because the DayRender event is raised while the Calendar control is being rendered, you cannot add a control that can also raise an event, such as LinkButton. You can only add static controls, such as LiteralControl, Label, Image, and HyperLink.

You may search around for another event to use instead, but you won’t find one.  The calendar is only created during the render process, there’s no collection of controls to manipulate earlier.  It was never designed for that kind of manipulation.  There are some commercial controls with more advanced functionality that could be used, but this is a relatively easy (and free) solution for simple scenarios.

The Workaround

The workaround for this is relatively simple:

  • Add a static control in the DayRender event
  • Make it behave like a dynamic control that has events
  • Pass data to identify which button was clicked.

For this example I’ve set up a simple database of activities, and the appropriate activities for each day will be displayed in the Calendar.  First, set up a few controls:

   1: <asp:Calendar ID="Calendar1" runat="server" VisibleDate="03/01/2009"
   2:     CssClass="monthCalendar" BorderStyle="None" 
   3:     DayStyle-CssClass="dayStyle" OtherMonthDayStyle-CssClass="otherMonthStyle" 
   4:     TodayDayStyle-CssClass="todayStyle" DayHeaderStyle-CssClass="dayHeaderStyle" 
   5:     WeekendDayStyle-CssClass="weekendDayStyle" ShowTitle="false"
   6:     SelectionMode="None" ShowGridLines="false" 
   7:     OnDayRender="Calendar1_DayRender"></asp:Calendar>
   8: <asp:LinkButton ID="CalendarLinkButton" runat="server" CssClass="hide" 
   9:     OnClick="CalendarLinkButton_Click"></asp:LinkButton>
  10: <asp:Label ID="ResultsLabel" runat="server" CssClass="results"></asp:Label>

There’s nothing really special about the Calendar in this case, it’s mostly styling, but I have specified a handler for the DayRender event, which I’ll address shortly. 

The significant piece of this markup is the CalendarLinkButton.  This LinkButton will not be visible on the page (the “hide” CssClass sets display: none).  It’s simply a placeholder so we have a LinkButton whose Click event can be fired.  Instead of trying to attach event to LinkButtons in the Calendar, the CalendarLinkButton’s Click event will be fired for each activity link in the Calendar, passing an argument that identifies which activity was clicked.

Lastly, the ResultsLabel will simply display a message indicating which activity was clicked, just to show it’s working.

Now, on to the code.  First, the Calendar DayRender event handler.

   1: protected void Calendar1_DayRender(object sender, DayRenderEventArgs e)
   2: {
   3:     DataClassesDataContext db = new DataClassesDataContext();
   4:     var activities = from a in db.Activities where a.Date.Date == e.Day.Date
   5:                      select a;
   6:     foreach (Activity a in activities)
   7:     {
   8:         HyperLink link = new HyperLink();
   9:         link.Text = a.Name;
  10:         link.CssClass = "activity";
  11:         link.NavigateUrl = 
  12:             Page.ClientScript.GetPostBackClientHyperlink(CalendarLinkButton, 
  13:                 a.ActivityID.ToString(), true);
  14:         e.Cell.Controls.Add(link);
  15:     }
  16: }

I’m using Linq to SQL to pull data for this example, so the DataContext is created and a simple query is run to pull activities associated with the current day from the database.  For each activity that is found, a HyperLink is created and added to the Controls collection of the Calendar table cell.  This HyperLink is the “button” that’s added for each event.  All fairly standard code for adding static events to a Calendar Day.

The significant line here is setting the NavigateUrl of the HyperLink.  This is done through the GetPostBackClientHyperlink method which, according to the documentation:

Gets a reference, with javascript: appended to the beginning of it, that can be used in a client event to post back to the server for the specified control with the specified event arguments and Boolean indication whether to register the post back for event validation.

So, in this case GetPostBackClientHyperlink returns a string equivalent to that used as the href value in the <a> tag rendered for the CalendarLinkButton.  I’ve passed it the ActivityID to be used as the Event Argument, which will allow us to determine which event was clicked.  The last parameter registers the post back for event validation.  Without this set to true, every postback from one of the Calendar’s HyperLinks would result in the following exception:

Invalid postback or callback argument. Event validation is enabled using in configuration or <%@ Page EnableEventValidation="true" %>in a page...

To see the specifics of how this is rendered, the CalendarLinkButton renders a link like:

<a href="javascript:__doPostBack('CalendarLinkButton','')">

and the activity HyperLinks render links like (where the second argument, ‘1’ in this case, is the appropriate ActivityID):

<a href="javascript:__doPostBack('CalendarLinkButton','1')">

So, when the activity links are clicked, they will cause a post back that triggers the CalendarLinkButton’s Click event.  Within this event, the ActivityID can be retrieved from the __EVENTARGUMENT hidden field (through the Request.Form collection).  All of this can be seen in the handler for the CalendarLinkButton’s Click event handler, which simply takes the ActivityID, pulls relevant data from the database, and writes a status message to ResultsLabel.

   1: protected void CalendarLinkButton_Click(object sender, EventArgs e)
   2: {
   3:     int id = Convert.ToInt32(Request.Form["__EVENTARGUMENT"]);
   4:  
   5:     DataClassesDataContext db = new DataClassesDataContext();
   6:     var activity = (from a in db.Activities where a.ActivityID == id
   7:                      select a).Single();
   8:  
   9:     ResultsLabel.Text = String.Format("You selected {0} on {1}", 
  10:         activity.Name, activity.Date.ToShortDateString());
  11: }

The screenshot below shows the final results.

CalendarButton_771_651

Download sample website: CalendarButtonEvent.zip (567 kb)

Updated April 11, 2009: The attached sample website was updated to include an example with a custom LinkButton that simplifies the click event handling. See details here.

Tags:
Categories: Calendars
E-mail | Kick it! | DZone it! | del.icio.us Permalink | Comments (19) | Post RSSRSS comment feed

Comments (19) -


7/2/2009 11:00 AM
Simulation pret
Thanks a lot for this information. It will be very helpful to me.


7/6/2009 10:31 PM
Nicole Thompsen
GOOD WEBSITE


United States Laurie T 
8/16/2009 12:07 AM
Laurie T
Amazing!  Thanks just the issue I have been dealing with.  Thank you so much!


Canada albert opena 
9/5/2009 11:56 PM
albert opena
This excellent source code.... Million thanks....


United States Abhishek 
9/25/2009 1:39 AM
Abhishek
I am creating Dynamic GridView with the help of GridViewtemaplte class.
By this i am creating TemplateField which have Linkbuttons.I also added a event to the Linkbuttona. All is working fine, but when i click the Linkbutton then i am unable to get the Text of LinkButton...Please help me out


New Zealand Steve 
4/23/2010 6:25 AM
Steve
What a awsome  blog!


Philippines cris 
7/2/2010 12:58 AM
cris

great post!


2/21/2011 12:02 PM
Kaprysy
Nice blog!


France Fred 
2/27/2011 9:29 AM
Fred
I was very pleased to find this web-site.I wanted to thanks for your time for this wonderful read!! I definitely enjoying every little bit of it and I have you bookmarked to check out new stuff you blog post.

Fred


United States Sufyan 
3/3/2011 9:00 AM
Sufyan
good post. Solved my problem.


People's Republic of China bailey  
7/20/2011 10:02 AM
bailey
I want to using CheckBoxList instead of Linkbutton during DayRender ,what I can do ?


United States Scott 
2/29/2012 2:47 AM
Scott
Greate articel!
How to raise a popup confirm before postback?


Turkey escort bayan ankara 
6/22/2012 7:13 PM
escort bayan ankara
thank you admin my site http://www.eskortankara.org


Turkey escort bayan ankara 
7/6/2012 12:01 PM
escort bayan ankara


Turkey escort bayan ankara 
7/8/2012 9:37 AM
escort bayan ankara


Turkey Kamera sistemleri 
8/15/2012 5:37 AM
Kamera sistemleri
Thanks


Turkey ankara escort 
9/20/2012 7:13 AM
ankara escort


Turkey http://www.escort-ankara.org 
9/20/2012 7:14 AM
http://www.escort-ankara.org
thanks


Turkey ankara escort 
10/5/2012 11:14 AM
ankara escort

Pingbacks and trackbacks (1)+