Wrox Home  
Search
Professional ASP.NET 2.0 Server Control and Component Development
by Shahram Khosravi
August 2006, Paperback


Implementing ICallbackEventHandler

The AjaxNotifier control implements the ICallbackEventHandler interface to use the ASP.NET 2.0 client callback mechanism to make its asynchronous client callbacks to the server. Listing 3 presents the code for the RaiseCallbackEvent and GetCallbackResult methods.

Listing 3: Implementing the methods of the ICallbackEventHandler
    protected virtual string GetCallbackResult()
    {
      return callbackResult;
    }
    protected virtual void RaiseCallbackEvent(string eventArgument)
    {
      IDataSource ds = (IDataSource)Page.FindControl(DataSourceID);
      int notificationId = int.Parse(eventArgument);
      if (notificationId < 0)
        notificationId = 1;
      Page.Session["NotificationId"] = notificationId;
      if (ds != null)
      {
        DataSourceView dv = ds.GetView(string.Empty);
        dv.Select(DataSourceSelectArguments.Empty, SelectCallback);
      }
    }

AjaxNotifier exposes a string property named DataSourceID that the page developer must set to the value of the ID property of the desired tabular data source control. As Listing 3 shows, the RaiseCallbackEvent method uses the ASP.NET tabular data source control API to retrieve the data from the underlying data store in generic fashion as thoroughly discussed earlier in the book. The argument of the RaiseCallbackEvent method contains the notification ID of the latest notification that the current user has seen. Notice that the RaiseCallbackEvent method stores this notification ID value in the Session object. To help you understand why this is necessary, consider the page shown in Listing 4 where the AjaxNotifier control is used. Figure 1 shows what the end users see when they access this page.

Listing 4: A page that uses the AjaxNotifier control
<%@ Page Language="C#" %>
<%@ Register TagPrefix="custom" Namespace="CustomComponents" 
Assembly="CustomComponents" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<body>
  <form id="form1" runat="server">
    <custom:AjaxNotifier runat="server" DataSourceID="MySource" 
     DialogStyle-BackColor="LightGoldenrodYellow"
     DialogStyle-BorderColor="Tan" DialogStyle-BorderWidth="1px" 
     DialogStyle-CellPadding="2" DialogStyle-CellSpacing="0" 
     DialogStyle-BorderStyle="Groove" DialogStyle-ForeColor="Black"
     DialogStyle-GridLines="None" HeaderStyle-BackColor="Tan" 
     HeaderStyle-Font-Bold="True" 
	 AlternatingItemStyle-BackColor="PaleGoldenrod" />
    <asp:SqlDataSource runat="server" ID="MySource" 
     ConnectionString="<%$ connectionStrings:MySqlConnectionString %>"
     SelectCommand="Select * From Notifications Where Id > @Id">
      <SelectParameters>
        <asp:SessionParameter Name="Id" SessionField="NotificationId" 
         Type="Int32" />
      </SelectParameters>
    </asp:SqlDataSource>
  </form>
</body>
</html>

As Listing 4 shows, this page binds the AjaxNotifier control to a SqlDataSource control. Notice that the SelectCommand attribute of the <asp:SqlDataSource> tag contains the following Select SQL statement:

Select * From Notifications Where Id > @Id

This SQL statement contains a parameter named @Id whose value is the notification ID of the latest notification that the current user has seen. As Listing 4 shows, the SqlDataSource control uses a SessionParameter to retrieve the notification ID from the Session object. That is the reason that Listing 4 stores the notification ID in the Session object.

    <asp:SqlDataSource runat="server" ID="MySource" 
     ConnectionString="<%$ connectionStrings:MySqlConnectionString %>"
     SelectCommand="Select * From Notifications Where Id > @Id">
      <SelectParameters>
        <asp:SessionParameter Name="Id" SessionField="NotificationId" 
         Type="Int32" />
      </SelectParameters>
    </asp:SqlDataSource>

Session object is not the only available option to communicate data between a tabular data source control and posted data. Chapter 27 "Developing Ajax-Enabled Controls and Components: Asynchronous Client Callback" in the book Professional ASP.NET 2.0 Server Control and Component Development (Wrox, July-2006, ISBN: 0-471-79350-7) discusses another option to accomplish the same task. As Listing 3 shows, the RaiseCallbackEvent method registers the SelectCallback method as the callback for the Select data operation. Listing 5 presents the implementation of the SelectCallback method. The main responsibility of this method is to use the retrieved data record to generate the XML document that will then be sent to the client. In other words, the client and server exchange data in XML format.

Listing 5: The SelectCallback method
private void SelectCallback(IEnumerable data)
{
  using (StringWriter sw = new StringWriter())
  {
    using (XmlWriter xw = XmlWriter.Create(sw))
    {
      xw.WriteStartDocument();
      xw.WriteStartElement("notification");
      IEnumerator iter = data.GetEnumerator();
      if (iter.MoveNext())
      {
        PropertyDescriptorCollection col = 
                 TypeDescriptor.GetProperties(iter.Current);
        foreach (PropertyDescriptor pd in col)
        {
          if (pd.Name == "Source")
            xw.WriteElementString("source", 
			(string)pd.GetValue(iter.Current));
          else if (pd.Name == "Notification")
            xw.WriteElementString("summary", 
			(string)pd.GetValue(iter.Current));
          else if (pd.Name == "Id")
            xw.WriteElementString("id", 
			pd.GetValue(iter.Current).ToString());
        }
      }
      xw.WriteEndElement();
      xw.WriteEndDocument();
    }
    callbackResult = sw.ToString();
  }
}

Therefore, one of your responsibilities as an Ajax-enabled control developer is to decide on the structure or format of the XML document. Listing 6 presents an example of an XML document that the AjaxNotifier control supports.

Listing 6: Example of an XML document that AjaxNotifier supports
<notification>
  <id>3</id>
  <source>John</source>
  <summary>We'll meet tomorrow morning</summary>
</notification>

This XML document, like all XML documents, has a single document element, that is, <notification>, which contains three child elements: <id>, <source>, and <summary>.

As shown in Listing 5, the SelectCallback method uses the XmlWriter streaming API discussed in Chapter 24 "Developing Custom Role Providers, Modules, and Principals" in the book Professional ASP.NET 2.0 Server Control and Component Development (Wrox, July-2006, ISBN: 0-471-79350-7) to generate the XML document, which is then loaded into a StringWriter. The SelectCallback method first calls the WriteStartDocument method of the XmlWriter to signal the beginning of the document and to emit the XML declaration:

xw.WriteStartDocument();

It then calls the WriteStartElement method to write out the opening tag of the <notification> document element (see Listing 6):

xw.WriteStartDocument();

Next, it accesses the enumerator object that knows how to enumerate the retrieved data in generic fashion:

IEnumerator iter = data.GetEnumerator();

It then retrieves the PropertyDescriptionCollection collection that contains one PropertyDescriptor object for each datafield of the retrieved record:

PropertyDescriptorCollection col = 
	TypeDescriptor.GetProperties(iter.Current);

Next, it iterates through these PropertyDescriptor objects to write out the <source>, <summary>, and <id> elements and their contents:

if (pd.Name == "Source")
  xw.WriteElementString("source", 
  (string)pd.GetValue(iter.Current));
else if (pd.Name == "Notification")
  xw.WriteElementString("summary", 
  (string)pd.GetValue(iter.Current));
else if (pd.Name == "Id")
  xw.WriteElementString("id", 
  pd.GetValue(iter.Current).ToString());