09/23/2008

ASP.NET Ajax, JQuery & JSON Date Serialization

A little while back I came across a great post on how to use JQuery to do more efficient client side paging by Dave Ward. The sample shows you how to use JQuery to do Ajax callbacks for client side paging using a grid template. After downloading the demo and parsing through it all, I found a lot of things I really liked and even came across a little gotchya with the way ASP.NET serializes dates in JSON.

One part I really enjoyed about this sample is that your objects on the server are translated into client side objects. So Order.OrderID or Order.ShippingAddress.ShipName would work the same on the client and server side of the programming. The jtemplates add-in allows you to name your active object of the collection you are looping through a lot like .NET like so:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<tbody>
{#foreach $T.d as order}
<tr>
<td>
{$T.order.OrderID}
</td>
<td>
{$T.order.ShippingAddress.ShipName}
</td>
<td>
{DateDeserialize($T.order.OrderDate).format('MM/dd/yyyy')}
</td>
<td>
{DateDeserialize($T.order.RequiredDate).format('MM/dd/yyyy')}
</td>
<td>
{$T.order.ShippingAddress.ShipCountry}
</td>
</tr>
{#/for}
</tbody>

As you can see the order object is used in the jtemplates code just like in .NET code.

You may have noticed the DateDeserialize() function followed by the extension method .format() in the snipped above. This is due to some date deserialization issues I ran into. The signature for the DateDeserialize method is as so:

1
2
3
function DateDeserialize(dateStr) {
return eval('new' + dateStr.replace(/\//g, ' '));
}

This method is basically returning the evaluation of the date returned by the .NET framework (ex: /Date(894427200000)/), replacing the / with a space with the keyword "new" in front of it. This will return us a JavaScript Date object. Then I'm using the .format() extension method from the included MSAjax framework. It took me a little while to figure out, but once I did I fell in love. You can't put a price on full on object-oriented programming!

Special thanks to Dave Ward for opening my eyes to the power of JQuery!

Hope this helps!

View Comments
06/03/2008

Strongly Typed Dynamic User Controls

A short time ago I was confronted with a serious problem. What I needed to do was dynamically choose a UserControl as well as fire methods from that UserControl. The problem lies in the fact that a UserControl does not implement my custom methods that I needed for my controls. Each control was similar and would have the same methods but it would have different display characteristics.

That was when I had a small epiphany. Why can’t I just make an abstract base class? Well the answer is you can! Sometimes I am prone to forget how .NET allows me to customize pre-defined classes. What we can do is create an abstract base class that inherits the UserControl class, then have our UserControls inherit from our base class.

First we will create our abstract base class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;

/// <summary>
/// Base class to be inherited by a UserControl that displays the date
/// </summary>
public abstract class DateTimeDisplayControl : UserControl
{
/// <summary>
/// Updates the date time inside the user control.
/// </summary>
public abstract void UpdateDateTime();
}

So in the snippet above, we have created a class called DateTimeDisplayControl. This inherits from UserControl and will have to override the abstract method UpdateDateTime().

Now we can create a couple of UserControls that inherit from our DisplayDateTimeControl class. The first control will be called "ControlOne".

Here is the *.ascx code:

1
2
3
4
5
6
7
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="ControlOne.ascx.cs" Inherits="UserControls_ControlOne" %>
<p>User control one</p>
<asp:UpdatePanel ID="udp1" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<asp:Label ID="lblDateTime" runat="server" />
</ContentTemplate>
</asp:UpdatePanel>

Here is the code behind:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;

public partial class UserControls_ControlOne : DateTimeDisplayControl
{
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
UpdateDateTime();
}

public override void UpdateDateTime()
{
lblDateTime.Text = DateTime.Now.ToString();
udp1.Update();
}
}

As you can see, ControlOne contains an UpdatePanel with a Label inside of it. The Label will display the date and time. We will call our second control "ControlTwo" and it will look exactly like ControlOne, only it will say "User control two" inside of it.

Now we will create the actual *.aspx page to display the controls.

Here is the *.aspx code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml&quot;>
<head runat="server">
<title>Strongly Typed Dynamic User Controls</title>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="sm1" runat="server" />
<asp:UpdatePanel ID="udp" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<asp:Panel ID="pnlContent" runat="server" />
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="btnUseControlOne" EventName="Click" />
<asp:AsyncPostBackTrigger ControlID="btnUseControlTwo" EventName="Click" />
<asp:AsyncPostBackTrigger ControlID="btnUpdateDateTime" EventName="Click" />
</Triggers>
</asp:UpdatePanel>
<p>
<asp:Button ID="btnUseControlOne" runat="server" Text="Use Control One"
onclick="btnUseControlOne_Click" />&nbsp;
<asp:Button ID="btnUseControlTwo" runat="server" Text="Use Control Two"
onclick="btnUseControlTwo_Click" />&nbsp;
<asp:Button ID="btnUpdateDateTime" runat="server" Text="Update Content"
onclick="btnUpdateDateTime_Click" />
</p>
</form>
</body>
</html>

Here is the code behind:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
using System;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;

public partial class _Default : System.Web.UI.Page
{
private string _controlVirtualPath = string.Empty;
private DateTimeDisplayControl _DateTimeDisplayControl = null;

private string ControlVirtualPath
{
get
{
if (string.IsNullOrEmpty(_controlVirtualPath))
_controlVirtualPath = ViewState["ControlVirtualPath"].ToString();
ViewState["ControlVirtualPath"] = _controlVirtualPath;
if (string.IsNullOrEmpty(_controlVirtualPath))
throw new ApplicationException("The control virtual path was not found");
return _controlVirtualPath;
}
}

private DateTimeDisplayControl LoadedAjaxControl
{
get
{
if (_DateTimeDisplayControl == null)
{
_DateTimeDisplayControl = (DateTimeDisplayControl)Page.LoadControl(ControlVirtualPath);
}
return _DateTimeDisplayControl;
}
}

private void LoadAndDisplayUserControl()
{
pnlContent.Controls.Add(LoadedAjaxControl);
}

private void LoadAndDisplayUserControl(string controlVirtualPath)
{
_controlVirtualPath = controlVirtualPath;
pnlContent.Controls.Add(LoadedAjaxControl);
}

protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
LoadAndDisplayUserControl("~/UserControls/ControlOne.ascx");
}

protected void btnUseControlOne_Click(object sender, EventArgs e)
{
LoadAndDisplayUserControl("~/UserControls/ControlOne.ascx");
}

protected void btnUseControlTwo_Click(object sender, EventArgs e)
{
LoadAndDisplayUserControl("~/UserControls/ControlTwo.ascx");
}

protected void btnUpdateDateTime_Click(object sender, EventArgs e)
{
LoadAndDisplayUserControl();
LoadedAjaxControl.UpdateDateTime();
}
}

As you may see, the *.aspx page will load up ControlOne by default. There are 3 buttons on the page that will allow you to swap out ControlOne and ControlTwo as well as call the UpdateDateTime() method of the controls.

That is all there is to it! One important note is that I am using a private property to get the loaded control, this is important to note because you will need to call that UpdateDateTime() method on the instance of the control that you rendered to the page. I don’t know why I didn’t think of this long ago, but I hope you will find it as useful as I did!

Happy coding!

View Comments
04/14/2008

Clean Up ASP.NETs Head Tag With ControlAdapters

Ok, if you’re anything like me you absolutely gag when you see the rendered content of the ASP.NET head tag. It is all rendered out inline for some reason. I’m not 100% sure about the affects of it on web marketing, but I know one thing is for sure… It certainly doesn’t help your rankings any. At the very least it looks gross and it can be easily fixed with some portable c# files that you can include in any project. I found a reference here about it, so I picked it up and ran with it.

A couple of good things to note is the placement of some key pieces for control adapters in the .NET framework. The ControlAdapter class should be inherited for every control adapter you create, it can be found in the System.Web.UI.Adapters namespace. All of the tags we will be overriding belong to the System.Web.UI.HtmlControls namespace.

The first tag we will override is the head tag itself.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using System.Web.UI; 
using System.Web.UI.Adapters;
using System.Web.UI.HtmlControls;
namespace Rollem.ControlAdapters
{
public class HtmlHeadAdapter : ControlAdapter
{
protected override void Render(HtmlTextWriter writer)
{
HtmlHead headTag = (HtmlHead)this.Control;
writer.WriteBeginTag("head");
if (!string.IsNullOrEmpty(headTag.ID))
writer.WriteAttribute("id", headTag.ClientID);
writer.Write(HtmlTextWriter.TagRightChar);
foreach (Control item in headTag.Controls)
item.RenderControl(writer);
writer.WriteLine("");
}
}
}

Next we are on to the title tag.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using System.Web.UI; 
using System.Web.UI.Adapters;
namespace Rollem.ControlAdapters
{
public class HtmlTitleAdapter : ControlAdapter
{
protected override void Render(HtmlTextWriter writer)
{
writer.WriteLine();
writer.WriteFullBeginTag("title");
writer.Write(Page.Title);
writer.WriteEndTag("title");
writer.WriteLine();
}
}
}

Now the link tags.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using System.Collections; 
using System.Web.UI;
using System.Web.UI.Adapters;
using System.Web.UI.HtmlControls;
namespace Rollem.ControlAdapters
{
public class HtmlLinkAdapter : ControlAdapter
{
protected override void Render(HtmlTextWriter writer)
{
HtmlLink linkTag = (HtmlLink)this.Control;
writer.Write("<link");
AttributeCollection attributes = linkTag.Attributes;
IEnumerator keys = linkTag.Attributes.Keys.GetEnumerator();
while (keys.MoveNext())
{
string key = (string)keys.Current;
if (key.ToLower() == "href" && attributes[key].Contains("~"))
writer.WriteAttribute(key, linkTag.ResolveClientUrl(attributes[key]));
else
writer.WriteAttribute(key, attributes[key]);
writer.WriteLine(" />");
}
}
}
}

Last but not least, the meta tags.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using System.Web.UI; 
using System.Web.UI.Adapters;
using System.Web.UI.HtmlControls;
namespace Rollem.ControlAdapters
{
public class HtmlMetaAdapter : ControlAdapter
{
protected override void Render(HtmlTextWriter writer)
{
HtmlMeta metaTag = (HtmlMeta)this.Control;
writer.WriteBeginTag("meta");
if (!string.IsNullOrEmpty(metaTag.HttpEquiv))
writer.WriteAttribute("http-equiv", metaTag.HttpEquiv);
if (!string.IsNullOrEmpty(metaTag.Name))
writer.WriteAttribute("name", metaTag.Name);
writer.WriteAttribute("content", metaTag.Content);
writer.WriteLine(HtmlTextWriter.SelfClosingTagEnd);
}
}
}

Now all we have to do is setup a *.browser file in the App_Browsers folder to map the control adapter overrides.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<browsers>     
<browser refID="Default">
<controlAdapters>
<adapter controlType="System.Web.UI.HtmlControls.HtmlHead"
adapterType="Rollem.ControlAdapters.HtmlHeadAdapter" />
<adapter controlType="System.Web.UI.HtmlControls.HtmlTitle"
adapterType="Rollem.ControlAdapters.HtmlTitleAdapter" />
<adapter controlType="System.Web.UI.HtmlControls.HtmlMeta"
adapterType="Rollem.ControlAdapters.HtmlMetaAdapter" />
<adapter controlType="System.Web.UI.HtmlControls.HtmlLink"
adapterType="Rollem.ControlAdapters.HtmlLinkAdapter" />
</controlAdapters>
</browser>
</browsers>

Now if we right click and view source we will see a nice clean head tag.

Don’t live a moment longer with that ugly ASP.NET rendered head tag! 

Hope this helps!

View Comments