3.23.2009

JavaScript, DataGrid Paging, UpdatePanel, and ClientScriptManager. What works and what does not?

So, you want to add some simple JavaScript to an image to get the nice "hover" feature used on our homepage (hover over a small graph and you get a larger graph.)

It seems like a simple concept;

  • register your helper method

  • register your hover-over and hover-out images. Use JavaScript image to prevent caching issues "var myImg = new Image();"

  • add some onmouseover and onmouseout attributes

  • presto!


So, you do all this and it works perfectly! Right, well not exactly.

The first item that will break this concept is Paging. By turning on Paging for the GridView you will notice that all hover image will be the same for each page. This is because the ClientID of the rendered control will be the same for the same Row of data. For example the First Row of the First Page will have a ClientID of GridView_ctl01_Image1 and the First Row of the Second Page will have the same ClientID. This problem can be solved by incorporating the UniqueKey property and including this when generating a uniqueid (see method below).

The second item that will break this concept is the UpdatePanel. By enclosing our GridView in an UpdatePanel you will find that the hover will only work for the first Page, not for any others. This appears to be caused by using the ClientScriptManager instead of the ScriptManager (highlighted in Yellow below.) This problem can be solved by only using the ScriptManager and NOT the ClientScriptManager.

Here is some sample code to illustrate what I have found

Sample ASPX Code
<body>
<form id="form1" runat="server">
<asp:XmlDataSource ID="XmlDataSource1" runat="server" DataFile="~/App_Data/GridData.xml"XPath="data/rows/row"></asp:XmlDataSource>
<asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"DataSourceID="XmlDataSource1"AllowPaging="True"PageSize="3">
<Columns>
<asp:TemplateField HeaderText="Name">
<ItemTemplate>
<asp:Label ID="Label1" Text='<%# XPath("Name") %>' runat="server"></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Image">
<ItemTemplate>
<cus:CustomImage ID="CustomImage1" AlternateText='<%# XPath("Name")%>' runat="server" UniqueKey='<%# XPath("Id")%>' ImageUrlSmall='<%#XPath("UrlSmall") %>' ImageUrlLarge='<%#XPath("UrlLarge")%>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</ContentTemplate>
</asp:UpdatePanel>
</form>
</body>

Sample C# Code
/// <summary>
/// Summary description for CustomImage
/// </summary>

public class CustomImage : Image
{
[Browsable(true)]
public string ImageUrlLarge{
get;
set;
}
[Browsable(true)]
public string ImageUrlSmall{
get;
set;
}
[Browsable(true)]
public string UniqueKey{
get;
set;
}
public CustomImage()
: base() { }
protected override void OnPreRender(EventArgs e){
base.OnPreRender(e);
ClientScriptManager csm = this.Page.ClientScript;
if (!csm.IsStartupScriptRegistered(this.GetType(), "Shared")){
StringBuilder sb = new StringBuilder();
sb.AppendLine("function switchImageUrl(object, image) {");
sb.AppendLine(" object.src = image.src;");
sb.AppendLine(" object.style.zIndex = 1001;");
sb.AppendLine("}");
csm.RegisterStartupScript(
this.GetType(),
"Shared",
sb.ToString(),
true);
}
string key = GetUniqueKey();
if (!csm.IsClientScriptBlockRegistered(this.GetType(), key)){
StringBuilder sb = new StringBuilder();
sb.AppendFormat("var {0}large = new Image();\n", key);
sb.AppendFormat("{0}large.src = \"{1}\";\n", key, ImageUrlLarge);
sb.AppendFormat("var {0}small = new Image();\n", key);
sb.AppendFormat("{0}small.src = \"{1}\";\n", key, ImageUrlSmall);
// Works with Paging on, but not UpdatePanel
csm.RegisterClientScriptBlock(
this.GetType(),
key,
sb.ToString(),
true);
// Works with Paging and UpdatePanel
ScriptManager.RegisterClientScriptBlock(
this,
this.GetType(),
key,
sb.ToString(),
true);
}
this.Attributes.Add(
"onmouseover",
String.Format(
"switchImageUrl(this, {0}large)",
key));
this.Attributes.Add(
"onmouseout",
String.Format(
"switchImageUrl(this, {0}small)",
key));
if (!String.IsNullOrEmpty(ImageUrlSmall))
this.ImageUrl = ImageUrlSmall;
}
private string GetUniqueKey() {
// Client ID does work with Paging because
// paged rows end up with the same ClientID
// For Example:
// DataGrid_ctr01_CustomImage1 (page one row one)
// DataGrid_ctr01_CustomImage1 (page two row one)

if (String.IsNullOrEmpty(UniqueKey))
// Decided to hash the ClientID to avoid
// overly long var name in client stream

return String.Format(
"_ci_id_{0}_",
Math.Abs(this.ClientID.GetHashCode()));
else
// When UniqueKey is present use this instead
return String.Format(
"_ci_pk_{0}_",
UniqueKey);
}
}

No comments:

Post a Comment