Enable Customer Reviews
Create a custom control that adds reviewing and rating functionality to e-commerce sites.
by Fabio Claudio Ferracchiati

August 2003 Issue

Technology Toolbox: VB.NET, ASP.NET

Popular e-commerce sites, such as Amazon and eBay, allow users to review and comment on products available for sale on the site. This functionality lets customers consider impartial comments when they choose among products, and helps sellers understand customer preferences. I'll show you how to implement your own reviewing functionality by using a powerful technique you can reuse on any page—the ASP.NET custom control.

An ASP.NET custom control is an assembly that contains one or more classes that derive from the WebControl class in the System.Web.UI.WebControls namespace. Creating an ASP.NET custom control is similar to building a WinForms control: You can use the overridable Render method to provide a user interface and to display graphics. You can build the HTML representing your control's output in the Render method's body and use the HtmlTextWriter method's parameter to write the HTML into the ASP.NET page. You can also specify new properties and use VB.NET language attributes to specify default values, descriptions, VS.NET Properties window categories, and much more.

When a page containing the custom control loads, the control receives many sequential events you can trap to customize the component's behavior (see Table 1). You usually use the Init event to initialize the control's member variables and the Render event to write the HTML code into the ASP.NET page. You can implement IPostBackDataHandler and IPostBackEventHandler interfaces for your component class to add extra events, such as LoadPostData, which retrieves incoming form data so you can manage it before the control uses it.

Now I'll show you how to build the ReviewerCtrl custom control to add review functionality to your pages ( download the source code here). ReviewerCtrl comprises three classes. The AverageCtrl class, which derives from the WebControl class, displays an image representing the product's ratings average. The ListCtrl class, which also derives from the WebControl class, displays an HTML table containing either the last user comment or a list of all comments related to the specified product identifier. The Vote class, which derives from the Object class, provides the InsertReview method, which allows you to associate a new review to the related product.

All three classes have two common properties: The ProductID property specifies the product identifier you use to associate ratings and reviews to the product, and the XMLPath property specifies the path of the XML files where you store ratings and reviews. You could use a database to store them, but XML—in conjunction with ADO.NET classes—is the best solution when you have a small amount of information to manage, because you don't waste resources by connecting to a database, and you're ready to share your data through Web services.

Change the Tagname Value
The <cc1:tagname> tag is used by default when you insert the ReviewerCtrl component into an ASP.NET Web form. You can change this value by adding the TagPrefix Attribute class to specify the component namespace and the new prefix:

<Assembly: TagPrefix("ReviewerCtrl", _
   "VSM")>

You can also associate a new icon to a custom control by adding a 16-by-16 bitmap that has the same control class name and the Embedded Resource property. The control uses the new bitmap instead of the default one when you add the component to VS.NET's toolbox.

Take a look at each component the ReviewerCtrl assembly provides, starting with the ListReviews control. This component displays either an HTML table filled with all the reviews for the specified product, or the last review (see Figure 1). The component provides this property in order to know which kind of output to display in the final ASP.NET page:

<Bindable(True), _
   Category("Appearance"), _
   DefaultValue("True")> _
   Property LastReview() As Boolean
Get
   Return m_bLastReview
End Get
Set(ByVal Value As Boolean)
   m_bLastReview = Value
End Set
End Property

The Attribute classes you specify just before the property are useful for customizing the component's look and feel within VS.NET (see Table 2).

The component checks during the Render phase for the LastReview property in order to display the correct output (see Listing 1). You build a temporary string to concatenate the XMLPath and the product identifier and obtain the XML file associated with the product. Then, you use the ReadXml method to load the XML file into a strongly typed DataSet. Finally, you use the DataView object's Sort property to sort the DataSet content so that the first record corresponds to the last inserted review. If the LastReview property is set to False, a For Each loop uses the DataSet to display each record.

The Render method provides the HtmlTextWriter parameter, which contains the Write method. This method writes the specified string as HTML output in the ASP.NET page.

The AverageCtrl component calculates the average of all products' ratings by summing all ratings and dividing by the number of ratings, then shows the related HTML <IMG> image tag (see Figure 1). It adds the new number to a new DataSet column and uses it in a Select Case statement to pick the correct image. You use the Init event to create the new column, specifying the Double data type in order to manage decimal numbers:

Protected Overrides Sub OnInit( _
   ByVal e As System.EventArgs)

Dim dc As New DataColumn("Average", _
   System.Type.GetType( _
   "System.Double"))

m_dsReviews.REVIEW.Columns.Add(dc)

Sum and Average the Ratings
You use the Sum aggregate function in the Render event to sum all the ratings and calculate the average:

Protected Overrides Sub Render( _
   ByVal output As HtmlTextWriter)
…
m_dsReviews.ReadXml(strXMLPath)
   m_dsReviews.REVIEW. _ 
      Columns("Average").Expression = _
      Sum(Vote)"
Dim dAverage As Decimal = _
   m_dsReviews.REVIEW(0) _ 
   ("Average") / _
   m_dsReviews.REVIEW.Count

Finally, you round the average by a decimal place and use the resulting value to build the image name:

Select Case (Decimal.Round(dAverage, 1))
   Case 1.0
      strImage = strPrefix & "1.gif"

The control provides output types for book content sites, music content sites, and generic content sites. The control's Type property lets users select the output type by choosing from an Enum data type (see Listing 2).

The ReviewerCtrl component contains a class you can use to insert a new review, either by adding it to the existing XML product file or by creating a new one. The class provides the InsertReview method, which accepts three parameters: the author name, the review content, and the rating (see Listing 3). The method uses the product identifier and the XMLPath to build the XML filename. It provides the name to the constructor of the FileInfo class within the System.IO namespace. FileInfo contains the Exists method, which checks for the specified file's presence on the drive and returns a Boolean True value when the file is found. The component then opens the file, retrieves the last product identifier, and increments it by one. Finally, it adds a new row to the DataSet and uses the DataSet's WriteXML method to write the file.

An ASP.NET form uses the Vote class to insert a new review (see Figure 2). The code behind the Vote button is simple:

Dim vote As New ReviewerCtrl.Vote
vote.ProductID = 1
vote.XMLPath = "C:\"

vote.InsertReview(txtAuthor.Text, _
   txtReview.Text, _
   rblVotes.SelectedItem.Value)
Response.Redirect("default.aspx")

The preceding code creates a new Vote object and initializes it with a product identifier and XMLPath values. Then, it calls the InsertReview method to pass the parameters retrieved from the Web form. Finally, it shows the main page to display the last review inserted.

You can use ReviewerCtrl with your ASP.NET site simply by adding the assembly to the BIN directory and adding the component to VS.NET's toolbox. Leverage ASP.NET custom controls' reusability to add high-end features to your pages easily.



About the Author
Fabio Claudio Ferracchiati has 10 years of experience using Microsoft technologies. He's been focusing attention recently on the new .NET Framework architecture and languages and has written books for Wrox Press about this technology. He works in Rome for the CPI Progetti SpA company (www.cpiprogetti.it). Contact him at .