Sharing a RadDatePicker in a RadGrid

I recently had a requirement to allow a date to be updated from within a RadGrid, without any extra popups or such like.

So, I googled around and found this article.

However, it didn’t actually work as the telerik.getPreviousHtmlNode method didn’t exist, that may have been down to me using the Q2 2008 version of the controls and not the very latest version. I also found a problem with the dateSelected client-side event handler being called when I clicked on the pop-up toggle image for subsequent changes to dates other than the first one clicked on.

So, here is my solution. It’s a mix of jQuery, telerik jQuery extensions and plain old DOM manipulation.

C#

Main Page

This is all fairly standard stuff. I’m restricting the RadDatePicker to only allow dates of today onwards, I’m also setting up some string resources for use in my client-side javascript code and setting a javascript global variable to the ClientID of my single RadDatePicker.

 protected void Page_Load(object sender, EventArgs e)
 {
     ...

RadDatePicker1.MinDate = DateTime.Today;

ClientScript.RegisterClientScriptBlock(GetType(), "DatePickerID", "var datePickerClientID = '" + RadDatePicker1.ClientID + "';" + Environment.NewLine, true);

Dictionary text = new Dictionary();
     text.Add("AjaxUpdating", Resources.Ajax_Updating);
     text.Add("AjaxUpdateSuccessful", Resources.Ajax_UpdateSuccessful);
     text.Add("AjaxUpdateFailed", Resources.Ajax_UpdateFailed);

StringBuilder sb = new StringBuilder();
     sb.AppendLine("var text = new Array();");
     foreach (KeyValuePair textValue in text)
     {
         sb.AppendFormat("text['{0}'] = '{1}';{2}", textValue.Key, textValue.Value, Environment.NewLine);
     }
     ClientScript.RegisterClientScriptBlock(GetType(), "Strings", sb.ToString(), true);

...
 }
 

Update Page

This is the page that is executed by the ajax call. Again, fairly simple stuff; Pull in the values from the Request.Form collection, act on them and prepare a response object which is formatted into a json string in order for the client-side code to be able to act on the results easily.

 public partial class UpdateItemDate : System.Web.UI.Page
 {
     [DataContract]
     private class ResponseObject
     {
         [DataMember]
         public bool Success { get; set; }

[DataMember]
         public string ControlID { get; set; }

[DataMember]
         public string ActualDate { get; set; }
     }

protected void Page_Load(object sender, EventArgs e)
     {
         var response = new ResponseObject()
         {
             ControlID = Request.Form["ControlID"],
         };

int itemID = 0;
         DateTime newDate = DateTime.MinValue;

if (int.TryParse(Request.Form["ItemID"], out itemID) &&
             DateTime.TryParse(Request.Form["NewDate"], out newDate))
         {
             try
             {
                 DataProvider dataProvider = ServiceLocator.Current.GetInstance();
                 response.ActualDate = Request.Form["NewDate"];
                 response.Success = dataProvider.SetDate(itemID, newDate);
                 ServiceLocator.Current.GetInstance().SubmitChanges();
             }
             catch (Exception ex)
             {
                 ServiceLocator.Current.GetInstance().Error
                 (
                     ex,
                     string.Format("Cannot update date for item_id = {0}", itemID)
                 );
                 response.PromisedDate = Request.Form["OriginalDate"];
                 response.Success = false;
             }
         }
         else
         {
             response.ActualDate = Request.Form["OriginalDate"];
             response.Success = false;
         }

Response.Write(response.AsJsonDataContractString());
         Response.End();
     }
 }
 

Javascript

I have two global variables defined here. The currentTextBox keeps track of which textbox the newly selected date should go in to, the calendarOpen flag tells me whether the calendar pop-up is visible. For some reason the telerik methods always returned “yes” when I asked them the question!

Because of the layout of my webpage (it being inside a pop-up RadWindow), I fixed the location of the calendar pop-up to 10,10. This was to avoid complications with the calendar dissappearing off the edge of my pop-up window. I believe that Telerik are still working on making the calendar part of the RadDatePicker not dissappear off-screen.

The calendarOpen flag is what I had to add in order to stop the dateSelected() method (called by the RadDatePicker client-side whenever a date is selected in the calendar pop-up) from clearing the currentTextBox under certain circumstances. I set this flag to “false” in the showPopup() method, this is called when the toggle img tag is clicked on. I’ve also wired in the “calendarOpening” client-side event with just one line of code to set the flag to “true”.

This way I know for sure when the calendar pop-up is actually visible. Without that code the currentTextBox was getting clear whenever I clicked on a toggle img tag other than the first one I clicked on after the initial page load. Very odd behaviour, but this work-around seems to work nicely 🙂

The UpdateItemDate() method just sets the text of the currentTextBox to inform the user that something is happening. It then makes the ajax call, passing the ItemID, NewDate, OriginalDate and ControlID to the server-side. I’m using a callback here so that my “resetting” of the currentTextBox text only happens once an update has been performed. I have also specified “json” as the final parameter, this has the effect of automatically parsing the server response into an object and passing it to my callback method.

The UpdateItemDateCallback() method, takes two parameters. The second one isn’t actually used, according to what I’ve read this can only ever be “Success” anyway. See here for more details. I find the textBox control using jQuery, currentTextBox may not be correct anymore. I then set the text and colour accordingly. Finally, I set up a two second timer to put the text to what it should be, ie – data.ActualDate.

 var currentTextBox = null;
 var calendarOpen = false;

function showPopup(e)
 {
     calendarOpen = false;
     currentTextBox = e.srcElement.parentNode.firstChild;
     var datePicker = $find(datePickerClientID);
     datePicker.set_selectedDate(datePicker.get_dateInput().parseDate(currentTextBox.innerText));
     datePicker.showPopup(10, 10);
 }

function dateSelected(sender, args)
 {
     if (calendarOpen && currentTextBox != null)
     {
         var itemID = currentTextBox.parentNode.lastChild.previousSibling.innerText;
         var originalDate = currentTextBox.innerText;
         currentTextBox.innerText = args.get_newValue() + " ";
         UpdateItemDate(itemID, args.get_newValue(), originalDate);
     }
 }

function calendarOpening(sender, args)
 {
     calendarOpen = true;
 }

function UpdateItemDate(itemID, newDate, originalDate)
 {
     currentTextBox.innerText = text["AjaxUpdating"];
     $.post("UpdateItemDate.aspx", { ItemID: itemID, NewDate: newDate, OriginalDate: originalDate, ControlID: currentTextBox.id }, UpdateItemDateCallback, "json");
 }

function UpdateItemDateCallback(data, textStatus)
 {
     textBox = $("#" + data.ControlID);
     var str = data.Success ? "AjaxUpdateSuccessful" : "AjaxUpdateFailed";
     var colour = data.Success ? "Blue" : "Red";
     textBox.text(text[str]).css("color", colour);
     window.setTimeout("$('#" + data.ControlID + "').text('" + data.ActualDate + "').css('color', '');", 2000);
 }
 

HTML

This should all be fairly self-explanatory, the RadScriptManager is in there so we get the $find method and $telerik object to play with. Note the extra (hidden) <asp:Label /> tag, which is used to store my item_id.

 <body>
     ...
     <form id="form1" runat="server">
         ...
         <telerik:RadScriptManager ID="RadScriptManager1" runat="server">
             <Scripts>
                 <asp:ScriptReference Assembly="Telerik.Web.UI" Name="Telerik.Web.UI.Common.Core.js" />
             </Scripts>
         </telerik:RadScriptManager>
         <telerik:RadDatePicker ID="RadDatePicker1" runat="server" style="display:none;" DateInput-DateFormat="d MMM yyyy">
             <ClientEvents OnDateSelected="dateSelected" OnPopupOpening="calendarOpening" />
         </telerik:RadDatePicker>
         <radG:RadGrid ID="myGrid" runat="server">
             <MasterTableView>
                 <Columns>
                     ...
                     <radG:GridTemplateColumn>
                         <HeaderTemplate>
                             <asp:Literal ID="myDateHeading" runat="server" Text='<%# Resources.myDateHeading %>'></asp:Literal>
                         </HeaderTemplate>
                         <ItemTemplate>
                             <div class="myDate">
                                 <asp:Label ID="myDate" runat="server" Text='<%# Eval("my_date", "{0:d MMM yyyy}") >;' />
                                 <asp:Image ID="popupImage" runat="server" ImageUrl="~/images/datePickerPopup.gif" onclick='showPopup(event)' />
                                 <asp:Label ID="itemId" runat="server" CssClass="itemId"><%# Eval("item_id") %></asp:Label>
                             </div>
                         </ItemTemplate>
                     </radG:GridTemplateColumn>
                 ...
                 </Columns>
             </MasterTableView>
         </radG:RadGrid>
         ...
     </form>
     ...
 </body>
 

CSS

And finally a little bit of CSS to make it look nice!

 .myDate
 {
     white-space: nowrap;
     display: inline;
     position: relative;
     width: 100%;
 }

.myDate span
 {
     margin-right: 30px;
 }

.myDate img
 {
     float: right;
     margin-top: -3ex;
 }

.myDate .itemId
 {
     display: none;
 }
 

Well, I hope that helps someone out, as well as serving as a reminder for me in the future should such a requirement arise again!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s