Building a Simple Rating Control for ASP.NET 2.0

UPDATE: 16-7-2008 I have updated the control and wrote a new article about it. This new release fixes a few bugs and comes with a better sample web site to try out the control. Check out the new article here.



To show visitors of your site how other visitors feel about the content you're presenting on your site, it's good idea to let your visitors rate your content.

Many web sites take this approach. For example, Amazon uses a 5 star rating approach to rate the articles they're selling. Microsoft's MSDN site uses a nine-bar graph to display the quality of their (technical) articles. My own site uses a five-bar graph to let users rate an article (in the left hand column). Since there are many uses for a rating feature, it makes sense to encapsulate the rating mechanism in a custom ASP.NET Server Control. This article shows you how to build such a control.

Prerequisites

The article assumes you're familiar with C# and ASP.NET 2.0, with the control architecture in particular. However, even if you don't understand all of it, you can just rip the code from this article and use the control directly in your pages.

Introduction

The control (called ContentRating) I am going to build in this article is used to display the rates for a content item and to allow a user to rate an item. It will present the rating with a number of star images like this:

A ContentRating with Four and a Half Stars
Figure 1 - The ContentRating Control

In this example, the control display an average rating value of 4.5. It shows four full images, one half image and one half "disabled" image. Besides showing the average rate for the content item, a user can also click one of the images to rate the item. When they click the item, the page posts back to the server and raises an event that a page developer can handle to store the rate value in a data store like a database or an XML file.

The control does not deal with retrieving and storing the actual rate values in a data store as this is often quite site specific. However, it does show you where to put your own logic to make this possible.

Before I started building this control, I made a short list with the functionality I wanted it to have. In particular, the control

  • must use images (like a star) to indicate the quality of the content item
  • must be able to display an arbitrary number of stars. Many sites use a five-star approach but you should also be able to give it, say, 10 stars
  • must raise an event to indicate to the page that the user is about to rate an item. It must also raise an event after an item has been rated.
  • must be easy to use, without much configuration. Therefore, it should come with a default set of star images.
  • must be easy to deploy. Ideally, all you want to deploy is a single .dll file, so the control must embed the default images.
  • must keep track of the content items that a user has already rated, to stop a user from rating the same item more than once.
     

In this requirements list, you'll see a number of issues that make the development of this control an interesting journey. Embedding images in a control is very useful, but can be a bit tricky at first. Also, keeping track of rated items will be an interesting challenge. I'll show you how to accomplish these tasks in the remainder of this article.

Control Design

Let's start by looking at the important classes that make up the ContentRating control. First, there's the ContentRating class itself. This class inherits from CompositeControl, a new control base class in ASP.NET 2 that makes it easy to build controls that contain child controls. I have chosen this class because the ContentRating control contains images (the rating stars) that fire their own postbacks. By inheriting from CompositeControl, you get some stuff for free that you previously had to create yourself. One of the things that CompositeControl does is implement the INamingContainer interface that ensures that child controls automatically get unique control names. The following images shows the full class diagram for ContentRating.

The Class Diagram of the ContentRating Class

Figure 2 - The Class Diagram of the ContentRating Class

The class has seven properties, seven methods and two events, each of which will be described below.

Properties

Property Name Description
DataSource

An array of integers that holds the values for the rated items. The array could look like this:

{2, 0, 0, 5, 5}

This means that 2 users rated the article with a 1, five users gave it four stars and another five users gave it five stars.
 

HalfStarImage The image to be displayed to indicate a half star. This image is used when the average rating is between x.3 and x.7. So an average rating of 3.56 displays 3 full images, one half selected image and 1 and a half disabled image.
ItemId An object that holds the unique ID of the content item. This can be an int or a GUID and so on.
LegendText The text below the rating stars.
OneStarImage The image that is displayed for a single selected star.
OneStarImageDisabled The image that is displayed for a disabled star.
TagKey The TagKey for the control. By default, CompositeControl renders as a <span> tag but I have overridden the TagKey property to make the control render as a <table> instead.

 

In addition to these properties, the ContentRating class also has a number of methods:

Methods

Method Name Description
CreateChildControls Inherited from Control (where CompositeControl ultimately inherits from) and overridden to call the private CreateControlHierarchy method that builds the child controls.
CreateControlHierarchy A private helper method that builds up the child control hierarchy, by adding the star images to an HTML table row.
DataBind Inherited from Control and overridden to store the DataSource in ViewState and then call CreateControlHierarchy to build up the control hierarchy.
GetImageUrl A private helper that returns the full virtual URL for an image. If one of the public properties, like OneStarImage has been set, it uses that value to build up the URL. Otherwise, it used Page.ClientScript.GetWebResourceUrl to get a reference to one of the embedded images.
OnBubbleEvent Inherited from Control and overridden to see if the object that raised an event is one of the star images. If that's the case, the control fires its own events and then cancels the remainder of event handling by returning true.
OnRated Called from OnBubbleEvent when a user has rated a content item.
OnRating Called from OnBubbleEvent when user is about to raise a content item.

 

The control also exposes two events that allow a page developer to interact with the control at run-time

Events

Event Name Description
Rating Fires when a user has clicked one of the star images to rate the item, but before the actual Rated event is called. This allows a page developer to diagnose the RateEventArgs object that is passed and optionally cancel the Rated event (for example, if the user has already rated the item).
Rated Fires when a user has rated a content item.

 

We'll look at some of the inner workings of these properties, methods and events later in the article.

Another important class in the control is the RateEventArgs class. An instance of this class is passed to the page in the Rating and Rated events. A page developer can diagnose this class and then decide to cancel the Rated event, for example when a user has already rated the item. In the Rated event, this class exposes the rate value as an int. With the RateValue property, the page developer can update the database and then rebind the control to the new values. Here's the full diagram for the RateEventArgs class:

The Class Diagram of the RateEventArgs Class

Figure 3 - The Class Diagram of the RateEventArgs Class

The Cancel property allows a page developer in the Rating event to cancel the Rated event. The HasRated property determines whether the current user has already rated the current content item. The ContentRating control keeps track of rates for each user through cookies, so when a cookie for the current content item is found, this property is set to true. In the Rating event, you can set Cancel to true when HasRated is true to stop a user from rating the same item twice.

The RateValue contains the actual value that a user has chosen. This property (an int) always falls between 1 (the lowest possible rate value) and the maximum length of the DataSource array.

Finally, the SupportsCookies property is useful in the Rating event as well. When this property is false, it means the current user agent doesn't support cookies so the ContentRating control cannot keep track of the items a user has rated. In that case, you can use the Rating event to determine whether the user has already rated the item before by some other means, for example, by looking in your database.

The final class worth looking at is the RatingSettings class. This class shields you from the complexity of calculating averages, total scores and whether or not to display a "half image".

The Class Diagram of the RatingSetting Class

Figure 4 - The Class Diagram of the RatingSettings Class

To use this class, you need to pass an array of integers to its constructor that represents the selected rates. From there, the class calculates the Average, the TotalRates, the TruncatedValue and whether or not you should display a half star image. To see how this works, let's look at an example.

Suppose your rate array looks like this:

ContentRating1.DataSource = new int[] {2, 0, 0, 5, 5};

This means that 2 users rated the article with a 1, five users gave it four stars and another five users gave it five stars. This in turn means that 12 ratings have been made (TotalRates) with an average of 3.91 (2 * 1, plus 5 * 4 plus 5 * 5 divided by 12) . In this case, the control should display four full images and one disabled image. The Average property in this case will contain 3.91; the TotalRates is 12, the TruncatedValue is 4 and the ShowHalfImage is false. The rating will look like this in the browser: A ContentRating with Four Stars

Let's look at another example :

ContentRating1.DataSource = new int[] {2, 0, 0, 5, 13};

In this case, Average is 4.35; TotalRates is 20 and TruncatedValue is 4 (4.35 rounded down). However, because the fractional part of the average is between 0.3 and 0.7, the ShowHalfImage is set to true. In this case, the control displays four full images and one half image, like this:A ContentRating with Four and a Half Stars

If you want to know how the calculation is performed, take a look at the constructor of the RatingSettings class in the file RatingSettings.cs. You can download the full source for the ContentRating control at the end of this article.

The Inner Workings of the ContentRating Control

Now that you have seen some of the important classes used in the ContentRating control, it's time to look at some of its code. I won't show you all the code, but instead focus on some of the important concepts. Remember, you can download the full source at the end of the article.

Let's start by looking at the class declaration:

[
DefaultProperty("OneStarImage"),
DefaultEvent("Rate"),
ToolboxData("<{0}:ContentRating runat='server' />"),
ToolboxBitmap(typeof(ContentRating), "Resources.ContentRating.bmp")
]
public class ContentRating : CompositeControl
{

As I mentioned earlier, the control inherits from CompositeControl, a useful base class for classes that contain child controls. I also added a number of attributes to the class. The ToolboxData item determines how the control presents itself in the markup of an ASPX page. The {0} placeholder is replaced by the control's TagPrefix which is defined in the AssemblyInfo.cs file in the project:

[assembly: TagPrefix("Spaanjaars.Toolkit", "isp")]

When you add a control on your page by dragging it from the toolbox, you get the following markup:

<isp:ContentRating ID="ContentRating1" runat="server" />  

Another interesting attribute is ToolboxBitmap which tells the compiler what image to use for the control in the Toolbox in Visual Studio. For this to work, it's import you set the image's Build Action to Embedded Resource (more on this later). Also note that, because the images are stored in a folder called Resources, I prefixed the image name with Resources. Otherwise, the image won't be found and you end up with the default gear icon.

Right below the class declaration you find a region called Private Variables that holds a number of private fields used throughout the class. All of them are used as backing variables for the control's public properties.

Below the private variables you find a region called Events. This region contains two events: Rating and Rated that fire before and after a user has rated an item in the control. The events are setup using standard C# event handling code using add and remove to keep track of a list of subscribers to the event. For more information about this event handling syntax, get yourself a copy of the book "Professional ASP.NET 2.0 Server Control and Component Development" referenced later in this article in the "Used Resources" section.

The actual events are raised in the OnBubbleEvent method which is called when one of the child ImageButtons is clicked. I'll show you what goes on in this method after I discussed the other code regions in the control.

The Public Properties region contains the seven properties that you saw in Figure 2. The properties are all pretty straight forward, except that in the set block a call is made to this.EnsureChildControls to ensure that the child controls have been created. This is useful for design time support where you want the design surface to reflect any changes you make to the control. For example, when you set the OneStarImage property, the design surface shows your new image immediately.

The region Databinding and Control Creation is the most interesting one. It contains the methods for data binding and to create the control hierarchy. Let's start with the DataBind method:

public override void DataBind()
{
  if (ItemId == null)
  {
    throw new ArgumentException(@"Can't bind a datasource 
             without a valid ItemId");
  }

  if (dataSource == null)
  {     
    dataSource = new int[] { 0, 0, 0, 0, 0 };
  } 

  base.DataBind();
  Controls.Clear();
  ClearChildViewState();
  TrackViewState();
  ViewState["RateItems"] = dataSource;
  CreateControlHierarchy(dataSource);
  ChildControlsCreated = true;
}		

This method first determines if the ItemId has been set; without this ID there isn't much to display so an error is thrown instead. Notice that it isn't illegal to have null as the dataSource. It should be possible to display a complete blank rating control when no user has rated the content item before. In that case, the dataSource is set to a default integer array for 5 stars with all zeros.

The code then calls base.DataBind() so the DataBinding event of the control is raised. It then clears the entire child control collection, stores the dataSource in ViewState and then calls CreateControlHierarchy which is responsible for creating the child controls. This method accepts the dataSource as an argument and uses it internally to build up the correct child control collection.

Something similar is performed in the CreateChildControls method:

protected override void CreateChildControls()
{
  Controls.Clear();
  if (ViewState["RateItems"] != null)
  {
    int[] tempDataSource = (int[])ViewState["RateItems"];
    CreateControlHierarchy(tempDataSource);
  }
  else
  {
    if (this.DesignMode)
    {
      // We're displaying on a design surface, 
      // so make up a fake datasource.
      int[] tempDataSource = new int[] { 18, 23, 17, 12, 45 };
      CreateControlHierarchy(tempDataSource);
    }
  }
}		

The code first checks if the data source (RateItems) has been cached in ViewState. This would be the case on PostBack of the control. Depending on the page developer, DataBind may or may not be called on PostBack. If it isn't, the control is still able to display its ratings from the data source from ViewState. Of course, it's important that view state is enabled on the page for this to work.

If there's no ViewState and the control is currently in DesignMode (meaning it's visible on the design surface of Visual Studio), the code makes up a dummy data source with 5 random numbers.

In both cases, the dataSource is passed to the CreateControlHierarchy method.

The first thing that method does is creating an instance of the RatingSettings class by passing it the dataSource in its constructor:

RatingSettings mySetings = new RatingSettings(theDataSource);

As I explained earlier, this triggers some calculation code that fills the public (and read-only) properties of the RatingSettings class like TruncatedValue and Average.

It then creates a new TableRow object and starts looping for each item (an int) in the data source. Inside the loop, a new TableCell and an ImageButton is created. The ImageButton gets a CommandArgument and a CommandName to keep track of what rating value the image belongs to:

starImage.CommandArgument = numberOfCells.ToString();
starImage.CommandName = "Rate";

The numberOfCells variable loops from 1 through the length of the dataSource array, so you end up with a CommandArgument of 1, 2, 3 and so on.

Next, the code determines what image to display. Remember, there are three options: the full image, the half image and the disabled image:

if (numberOfCells <= mySetings.TruncatedValue)
{
  imageUrl = GetImageUrl(oneStarImage, Constants.RatingOneStarImage);
}
else
{
  if (numberOfCells == mySetings.TruncatedValue + 1 
          && mySetings.ShowHalfImage)
  {
    imageUrl = GetImageUrl(halfStarImage, Constants.RatingHalfStarImage);
  }
  else
  {
    imageUrl = GetImageUrl(oneStarImageDisabled, 
           Constants.RatingOneStarDisabledImage);
  }
}
starImage.ImageUrl = imageUrl;		  

When the item being added (numberOfCells) is smaller than or equal to the TruncatedValue, a full image is shown. So, when the average rating is 3.6, the TruncatedValue is 3 and thus three full images will be added. The next if statement sees if the half-star image should be added. It does that by looking at the ShowHalfImage property, but only when the numberOfcells is equal to TruncatedValue + 1. With an average of 3.6, the TruncatedValue is 3 and ShowHalfImage is true, so the fourth image will be the half-star image.

Finally, when both if blocks didn't fire, the oneStarImageDisabled is added.

In each if block, a helper method called GetImageUrl is used. This method look like this:

private string GetImageUrl(string imageUrl, string embeddedImage)
{
  string localImageUrl;
  if (String.IsNullOrEmpty(imageUrl))
  {
    localImageUrl = this.Page.ClientScript.GetWebResourceUrl
                                  (this.GetType(), embeddedImage);
  }
  else
  {
    localImageUrl = base.ResolveUrl(imageUrl);
  }
  return localImageUrl;
}		

When the imageUrl has been set (for example oneStarImage) its full virtual path is returned by calling base.ResolveUrl and passing in the imageUrl. However, when the property hasn't been set, the control uses the default images embedded in the assembly. It does this by calling Page.ClientScript.GetWebResourceUrl and passing it the full name of the image. To make the code a bit easier to manage, I have used constants (defined in the file Constants.cs) to refer to the embedded images, like this:

internal static class Constants
{
  internal const string RatingOneStarImage = 
          "Spaanjaars.Toolkit.Resources.OneStar.gif";
  internal const string RatingOneStarDisabledImage = 
          "Spaanjaars.Toolkit.Resources.OneStarDisabled.gif";
  internal const string RatingHalfStarImage = 
          "Spaanjaars.Toolkit.Resources.HalfStar.gif";
}  

This way, you don't need to remember the full "path" to the image, but you can refer to it by its name: Constants.RatingOneStarImage for example.

To make the embedded images work with Page.ClientScript.GetWebResourceUrl, you need to do two things:

  1. Set the image to Embedded Resource in its Build Action property:

    The Solution Explorer Showing an Embedded Resource
    Figure 5 - The Solution Explorer and Properties Pane Showing Embedded Images

     
  2. Tell the compiler you really want to expose the embedded image to the outside world. To do that, you need to include it in the AssemblyInfo.cs file like this:
    [assembly: WebResource(Constants.RatingHalfStarImage, "image/gif")]
    [assembly: WebResource(Constants.RatingOneStarDisabledImage, "image/gif")]
    [assembly: WebResource(Constants.RatingOneStarImage, "image/gif")]
    
    Notice how I am using the same Constants image keys again to refer to the full "namespace" of the image. Without these WebResource directives, you won't get a compiler error, but your images will result in 404 requests.
     

Near the end of the loop in the CreateControlHierarchy method, the ImageButton is added to the TableCell, and the TableCell is added to the TableRow. At the end of the method, the entire TableRow is added to the control's Controls collection. Finally, ChildControlsCreated is set to true, to indicate that the control now contains a valid child control hierarchy.

The final method that we need to look at is OnBubbleEvent that is fired when one of the child controls (the ImageButton controls) raises its Click event. Inside OnBubbleEvent the code sees if the event was raised by a command with its CommandName set to Rate. If that isn't the case, the code sets handled to false and exits. But if the event was raised by one of the ImageButton controls, the following code is executed:

RateEventArgs myArgs = 
               new RateEventArgs(Convert.ToInt32(ce.CommandArgument), true);
HttpCookie rateCookie;
cookieKey = String.Empty;
cookieValue = String.Empty;


// Try to set a cookie to indicate the user has rated this item.
if (Context.Request.Browser.Cookies == true)
{
  myArgs.SupportsCookies = true;
  cookieKey = "Rate_" + ItemId.ToString();
  cookieValue = ce.CommandArgument.ToString();

  rateCookie = Context.Request.Cookies["Rate"];

  if (rateCookie == null)
  {
    myArgs.HasRated = false;
  }
  else
  {
    if (rateCookie.Values[cookieKey] == null)
    {
      myArgs.HasRated = false;
    }
    else
    {
      myArgs.HasRated = true;
    }
  }
}
else
{
  myArgs.SupportsCookies = false;
}

This code checks if there is a cookie called Rate. If the cookie isn't there, it means the user has never rated a content item before. But if there is a cookie, the code continues to check if there is a cookie key for the current ItemId. The code uses a multi value cookie called Rate where each cookie key is named Rate_ItemId. Depending on the presence of that cookie, HasRated is set to either true or false.

The final bits in the OnBubbleEvent method deal with firing events:

// Raise the Rating event. 
OnRating(myArgs);	

As soon as OnRating is called, any event handler code in the APSX page that subscribed to the OnRating event will run. As an argument to the event handler, the code gets an instance of the RateEventArgs class. With this instance, a page developer can determine whether she wants to cancel the Rated event, like this:

protected void ContentRating1_Rating(object sender, 
                 Spaanjaars.Toolkit.RateEventArgs e)
{
  if (e.HasRated)
  {
    e.Cancel = true;
  }
}  

By setting Cancel to true, the Rated event won't be fired. This way, you can prevent users from rating the same item twice. If you don't mind if a user rates an item more than once, you can choose not to handle the Rating event at all.

Finally, when Cancel isn't true, this code runs:

if (!myArgs.Cancel)
{
  // Create a new cookie value for this rate.
  rateCookie = new HttpCookie("Rate");
  rateCookie.Expires = DateTime.Now.AddMonths(6);
  rateCookie.Values.Add(cookieKey, cookieValue);
  Context.Response.Cookies.Add(rateCookie);
  // Raise the Rated event to notify clients.
  OnRated(myArgs);
  handled = true;
}

This eventually causes the Rated event to be fired which you can handle in your page. The argument passed to the event handler contains the selected value in its RateValue property. With this property, you can update your existing datasource. For example:

protected void ContentRating1_Rated(object sender, 
        Spaanjaars.Toolkit.RateEventArgs e)
{
  int rateValue = e.RateValue;
  Guid itemId = myContentItem.Id;
  Content.InsertRating(itemId, rateValue);
}		

Note that I just made up this code. The ContentRating control doesn't know anything about your data source. It doesn't understand where to get the rate values from or where to store a new rate value. This is entirely up to the page developer.

This concludes the design and implementation of the ContentRating class. Although the control itself doesn't deal with storing and retrieving rate values from the database, I'll look briefly at using the control in a page in the next section so you can get an idea of where to write code that retrieves the values from the database and to store the new rate value again.

Using the ContentRating Control

Using the ContentRating control is pretty straightforward. Follow these steps to get up and running in no-time:

  1. Compile the ContentRating control in Visual Studio 2005. Make sure you make a release build.

     
  2. Create a new web application in Visual Studio 2005.

     
  3. Open the Default.aspx page and switch to Design View. Right click the Toolbox (press Ctrl+Alt+X if it isn't visible) and click Choose Items. Browse to the DLL you created in the previous step. The control will show up in the Toolbox.

     
  4. Drag the control from the Toolbox on the Design Surface of your page. You'll see the control appear with 5 images (three full, one half and one disabled).

     
  5. Create event handlers for its Rating and Rated event.

     
  6. Add code to the Page_Load method to set the initial value of the ContentRating control. Where you get the data from is up to you; as long as the data is an integer array with the rate values.

     
  7. Call DataBind() to bind your data source to the control.
     

The following code snippet shows the code behind of a page that uses the ContentRating control. For demonstration purposes, it uses ViewState as the backing store. Obviously, in a real world application you would use a data store like a database to retrieve and store the rate values.
 

protected void Page_Load(object sender, EventArgs e)
{
  if (!IsPostBack)
  {
    ContentRating1.ItemId = Guid.NewGuid();
    ContentRating1.DataSource = Values;
    ContentRating1.DataBind();
  }
}

protected void ContentRating1_Rating(object sender,
          Spaanjaars.Toolkit.RateEventArgs e)
{
  if (e.HasRated)
  {
    e.Cancel = true;
  }
}

protected void ContentRating1_Rated(object sender,
           Spaanjaars.Toolkit.RateEventArgs e)
{
  
  int[] tempValues;
  tempValues = Values;
  tempValues[Convert.ToInt32(e.RateValue) - 1] += 1;
  Values = tempValues;
  ContentRating1.DataSource = Values;
  ContentRating1.DataBind();
}

public int[] Values
{
  get
  {
    object values = ViewState["Values"];
    if (values != null)
    {
      return (int[])values;
    }
    else
    {
      return new int[] { 17, 0, 0, 5, 5 };
    }
  }
  set
  {
    ViewState["Values"] = value;
  }
}			

And combined with this markup:

<isp:ContentRating 
ID="ContentRating1"
runat="server"
OnRated="ContentRating1_Rated"
OnRating="ContentRating1_Rating"
LegendText="{0} rates / {1} avg."
/>

you end up with the following control in your browser:

The ContentRating Control as it Appears in the Browser on Page Load

Figure 6 - The ContentRating Control as it Appears in the Browser on Page Load

If you click the fifth star, the control will change to this:

The ContentRating Control as it Appears in the Browser after PostBack

Figure 7 - The ContentRating Control as it Appears in the Browser after PostBack

If you click one of the stars again, nothing happens because the control knows you already rated this item.

Summary

Building controls can be a daunting task. However, once you get the hang of it, it becomes much easier. Once you mastered the learning curve for Custom Controls, they tend to become a viable solution much more often. Instead of recreating some functionality in every site you build, you can use Custom Server controls and drag and drop to build a fully functional site in no time.

Hopefully, this article got you enthusiastic for building controls. If you have suggestions or enhancement requests for this control, or used my ContentRating control to kick start development of your own controls, please let me know.

Enhancements and Extensions

This is just a very basic control and I can think of many enhancements and extensions, including:

  • Derive from CompositeDataBoundControl instead of from CompositeControl. The CompositeDataBoundControl class has some additional behavior targeted at data bound controls. I haven't really looked into this class much, so I don't know how useful it will be.

     
  • Extend the images with OnMouseOver behavior where each star lights up when you hover over it. That way, it's easier for a user to see with what value they're going to rate the item. You could do that by writing some JavaScript that changes the images when a user hovers of them. You can embed the JavaScript in the assembly similar to how I embedded the star images.

     
  • Add a TextBox control to the ContentRating so users can let you know *why* they gave the item a specific rating.

     
  • Offer the page developer different sets of images, for example with different colors or sizes. That way, a page developer can change all the images at once by choosing from a simple drop down list with image sets. Once again, you should embed all the images in the assembly and change GetImageUrl so it returns a correct path to the images.

I wrote the ContentRating control while writing this article, so it hasn't been used or tested extensively in a real-world application. If you use this control and run into problems, let me know and I'll try to fix them and update the article.

Used Sources

For the research for this article, I used the following resources:

Download Files


Where to Next?

Wonder where to go next? You can read existing comments below or you can post a comment yourself on this article .


Consider making a donation
Please consider making a donation using PayPal. Your donation helps me to pay the bills so I can keep running Imar.Spaanjaars.Com, providing fresh content as often as possible.



Feedback by Other Visitors of Imar.Spaanjaars.Com

On Monday, December 25, 2006 12:21:57 PM Kiruba said:
This article was very useful to me.
If you ave tis kind of articles in VS2005 DotNet, Please send me.
On Monday, December 25, 2006 1:56:16 PM Imar Spaanjaars said:
Hi Kiruba,

I don't understand what you mean. This article *is* about VS 2005 and .NET....

Cheers,

Imar
On Monday, December 25, 2006 11:53:03 PM Jesus Jimenez said:
Very nice article!!

I'm downloading the source code of the article to test and pay a bit with your contol.

Really cool, thanks!!

Jesus Jimenez
http://blogs.clearscreen.com/dtax
On Tuesday, December 26, 2006 1:19:02 AM Imar Spaanjaars said:
Great. Have fun with it....

Imar
On Thursday, December 28, 2006 11:12:45 AM Irfan said:
Excellent Article!!     Keep it up
On Wednesday, January 24, 2007 5:21:51 PM brion said:
If only you had a VB version too
On Saturday, February 03, 2007 12:06:33 PM Graham said:
Great artical! If you have an artical or information on how this "feedback section" was made then I believe that would make another great article.
On Sunday, February 04, 2007 10:26:07 PM Imar Spaanjaars said:
Hi Graham,

Thanks for the suggestion. This topic has been on my Todo list for quite some time, but I just haven't gotten to it. I'll see what I can do to move it up a little.... ;-)

Cheers,

Imar
On Tuesday, February 20, 2007 8:05:27 AM Sajjad said:
great man
On Wednesday, February 21, 2007 9:54:26 PM Ryan said:
How would you save the values (integer array) into a database?
On Monday, February 26, 2007 9:25:28 PM Imar Spaanjaars said:
Hi Ryan,

Sorry I overlooked this method, so I am late with my reply.

There are a few ways to do this. One is to create a table with 6 columns: the content ID, and 5 rating columns.
Then you create a stored procedure that acceptds the content ID and a value for the rating the user gave (I wouldn't be passing around the entire array if I were you)

Then in the stored procedure, just update the right colum by increasing it by one. E.g.:

IF (@rateId = 1)
BEGIN
  Update Rating SET Count01 = Count01 + 1 WHERE ContentId = @contentId
END

-- Other fields go here

I am sure there are other, more efficient ways to do this, but this one works, and is relatively easy to use.

Cheers,

Imar
On Tuesday, April 17, 2007 11:03:53 AM Mort said:
Nice article! I've only just imported the control into a test project, and I'm having a little trouble with the LegendText property. According to the help text, the {0} parameter gives med the number of users that have rated something. In my case, this parameter seems to give me the sum of all ratings instead. i.e.
Using a 1-5 star rating, the {0} parameter gives med a result = 8 for the following:

1: Rating = 3
2: Rating = 5

... where I was expecting {0} to give med  a result = 2.

The {1} parameter gives me the expected average of all the ratings.

A bug, a documentation mistake, or a hopeless rating control user (me!) ?
On Tuesday, April 17, 2007 11:37:43 AM Mort said:
Oops, my bad.
I was inserting the actual rating value into the array, instead of incrementing the value at the correct index.
On Tuesday, April 17, 2007 6:07:07 PM Imar Spaanjaars said:
Hi Mort,

Pfew.... I was already afraid it was the documentation... ;-)

Glad it's working.

Imar
On Friday, April 20, 2007 5:47:36 PM Jeff said:
Great article! Although I like the idea of the stars I'd like to do something like what you have in your sidebar with a bar chart and radio buttons. I think this gives the user a better idea of the quality of the content of the article. That, and I just like the look of the bar chart better personally...

Is there a tutorial you'd recommend for creating a rating control that produces a bar chart similar to what you have on your pages?

Oh! Almost forgot... I was wondering, is there any possibility you'll do an article on using CAPTCHA with a form like you have on this form?
On Friday, April 20, 2007 8:42:15 PM Imar Spaanjaars said:
Hi Jeff,

You'd be surprised how it easy it would be to convert the star mechanism into a bar chart.

My current bar chart was in fact the basis for my star rating control. Calculating the ratings is even easier; you get an array of 5 elements so you know how large the images should be based on the rates. Then all you need to do is calculate the relative percentages based on the total number of votes.

I then render a simple table with 1 row and five cells, each containing an image stretched to reflect its relative size to the others.

I can't advice on any good tutorials, but I am sure Google can.

With regards to the CAPTCHA: check out this article and follow the link to the article on the 15seonds.com site.

Cheers,

Imar
On Monday, April 30, 2007 4:41:57 PM SJ said:
i'm getting the following errors when i downloaded the file how do i sort them out

Error 2 It is an error to use a section registered as allowDefinition='MachineToApplication' beyond application level.  This error can be caused by a virtual directory not being configured as an application in IIS. C:\Documents and Settings\SJ\Desktop\ContentRating\TestWebSite\web.config 30


Error 1 ContentRating1:Unknown server tag 'isp:contentrating'. C:\Documents and Settings\SJ\Desktop\ContentRating\TestWebSite\Default.aspx C:\...\ContentRating\
On Monday, April 30, 2007 4:52:57 PM Imar Spaanjaars said:
Hi SJ,

Did you try opening the web site from within Visual Web Developer? Does it work then?

This error is usually caused by a misconfiguration of IIS. Most likely, the folder TestWebSite is not configured as an application in IIS. Subsequently, the site than gets the configuration from the root of the web site, but also tries to override some settings through its own web.config file. This is not allowed, and thus you get the error.

Searching Google for the exact error message:

"It is an error to use a section registered as allowDefinition='MachineToApplication' beyond application level"

brings up many useful articles.

Cheers,

Imar
On Tuesday, May 22, 2007 12:38:10 PM alyona said:
Great article, was very helpful. Thanks
On Friday, June 01, 2007 2:45:21 PM Nazanin said:
Hi,Thank you for your great article..
can you tell me more about using sql database for ContentRating control?
u said befor , but i couldn't understand:(
have a good day.
On Monday, June 04, 2007 10:52:45 AM Imar Spaanjaars said:
Hi Nazanin,

What was it that you didn't understand in this comment:

================================
There are a few ways to do this. One is to create a table with 6 columns: the content ID, and 5 rating columns.
Then you create a stored procedure that accepts the content ID and a value for the rating the user gave (I wouldn't be passing around the entire array if I were you)

Then in the stored procedure, just update the right column by increasing it by one. E.g.:

IF (@rateId = 1)
BEGIN
  Update Rating SET Count01 = Count01 + 1 WHERE ContentId = @contentId
END

-- Other fields go here

================================

It's a pretty straight forward process; create a table with 6 columns: 5 for the various ratings of type int and a ContentId for the foreign key pointing to your content item.

Then create a stored procedure that accepts two parameters: one for the ContentId and one for the rate. Then use the IF code I posted earlier to update the count for the relevant item, taking the ContentId into account in the WHERE clause as per my example.

Hope this helps,

Imar
On Wednesday, July 04, 2007 11:05:04 AM sharad Yadav said:
Hello sir,

This article of creating Rating control is very nice, but I just know about this control more like how to place this rating control in datalist


Thanking You

with Regards
Sharad N. yadav
On Wednesday, July 04, 2007 1:31:47 PM Imar Spaanjaars said:
Hi sharad,

I haven't tested the control in a databound control, but the principles should be the same.

Place the control in something like an ItemTemplate, handle the ItemDataBound event and assign the data source.

Hope this helps,

Imar
On Thursday, July 05, 2007 7:23:32 PM Joshua Starr said:
First of all, great control! Thanks for making it!

I am attempting to use this control in a databound datalist. I have it set to handle OnItemDataBound with the code below. I keep getting the message: Can't bind a datasource without a valid ItemId

I believe this is because there is not an ItemID assigned to the control until the below code runs. I have tried changing the handler to OnItemCreated and I get an Object Not Set to Instance of an Object. Am I missing something obvious?

Thanks!!

Joshua Starr

--------------
    Public Sub datalisthandlers(ByVal sender As Object, ByVal e As DataListItemEventArgs) Handles dataPhotos.ItemDataBound
        'Rating Control Handler
        If e.Item.ItemType = ListItemType.Item Then
            Dim ratingcontrol As Spaanjaars.Toolkit.ContentRating
            ratingcontrol = e.Item.FindControl("rating")

            If ratingcontrol.ToString <> Nothing Then
                Dim values() As Integer
                values = New Integer() {0, 0, 5, 5, 5}
                ratingcontrol.ItemId = Guid.NewGuid()
                ratingcontrol.DataSource = values
                ratingcontrol.DataBind()
            End If

        End If
    End Sub
On Thursday, July 05, 2007 7:32:44 PM Imar Spaanjaars said:
Hi Joshua,

I think there are a couple of things wrong:

ratingcontrol = e.Item.FindControl("rating")

results in ratingcontrol being a generic control, not a Rating control. You may want to try this instead:

If e.Item.FindControl("rating") IsNot Nothing Then
  ' Control found, now cast it to a ContentRating
  ratingControl = CType(e.Item.FindControl("rating"), ContentRating)
End If

Then there is the issue of the ItemType. The ListItemType could be either an Item or an AlternatingItem for odd and even rows. So this may work better:

If e.Item.ItemType = ListItemType.Item Or If e.Item.ItemType = ListItemType.AlternatingItem Then

Finally, assigning a random new Guid to the ItemId isn't going to do you any good. Shouldn't you use some unique ID of Item.DataItem that you are using in your data binding? Or is this just to make sure it works?

Imar
On Thursday, July 05, 2007 8:04:46 PM Joshua Starr said:
Thanks for your speedy response, it is greatly appreciated. I added in your suggestions for the alternating row and Ctype.

I am using the ItemCreated handler for this. ItemDataBound complains about needing an itemID to databind which makes sense.

The GUID was just to see if it would work. I have since changed that to:

ratingcontrol.ItemId = dataPhotos.DataKeys(e.Item.ItemIndex).ToString

I am still getting an Object reference not set to an instance of an object as soon as I attempt to databind with my array.

If you have any other ideas, I would greatly appreciate hearing them :)
On Thursday, July 05, 2007 8:11:40 PM Imar Spaanjaars said:
Hi Joshua,

Hard to tell without seeing the actual code run. The object reference could be due to the rating control not being found, or due to other errors.

My suggestion is to set a breakpoint at the beginning of the handler, hit F5 and then step through the code, investigating every variable until you find the problem.

One thing to check: did you set the DataKeys property to a valid column / property name?

And what are you binding to? You could use Item.DataItem to get a strongly typed reference to the item being bound to the row.

Cheers,

Imar
On Thursday, July 05, 2007 8:34:28 PM Joshua Starr said:
I finally figured out the issue -- you don't need to call ratingcontrol.databind() when you are using the ItemCreated handler since you are setting up the item's default properties. The ItemDataBound handler executes shortly after ItemCreated effectively taking care of the databinding.

For those interested, here is an example of my ASPX code followed by my codebehind.

Thanks again Imar, for creating such a cool control.

ASPX:
First, set OnItemCreated="datalisthandlers" in the datalist control HTML, then set a DataKeyField to the unique ID you set in your SQL Data Srouce. Finally, place this inside the ItemTemplate of your Datalist:

[isp:contentrating id="rating" runat="server" onrated="rating_Rated" onrating="rating_Rating" legendtext="{0} rates / {1} avg.">
</isp:contentrating]


VB.NET CODE:

Public Sub datalisthandlers(ByVal sender As Object, ByVal e As DataListItemEventArgs) Handles dataPhotos.ItemCreated
        'Rating Control Handler
        If (e.Item.ItemType = ListItemType.Item) Or (e.Item.ItemType = ListItemType.AlternatingItem) Then

            If e.Item.FindControl("rating") IsNot Nothing Then
                Dim ratingcontrol As ContentRating
                ratingcontrol = CType(e.Item.FindControl("rating"), ContentRating)

                Dim values() As Integer
                values = New Integer() {0, 0, 5, 5, 5}
                --Unique DataKey is below. Use for SQL parameter
                ratingcontrol.ItemId = dataPhotos.DataKeys(e.Item.ItemIndex)
              
                --Set below to SQL datasource with datakey as parameter
                ratingcontrol.DataSource = values
                ' ratingcontrol.DataBind()
            End If

        End If
    End Sub
On Thursday, July 05, 2007 9:30:32 PM Imar Spaanjaars said:
Hi Joshua,

You're welcome.

Glad it's working now and thanks for posting an update.

Cheers,

Imar
On Monday, August 13, 2007 3:32:22 PM Gufran Sheikh said:
Hi Imar,
Once more time greate article
but as your suggetion i tried simple Imagebutton rollover with javascript.
i add a javascript in imagebutton's attribute like
starImage.Attributes.Add("onmouseover", "this.src='"+someurl+"';")
starImage.Attributes.Add("onmouseout","this.src='"+imageurl+"';")
someurl and imageurl is the public properties.
but it is not working when i rollover on the star images the rollover effect is not working while i tried the above method on imagebutton placed on a page and its working.
i searched for rollover effect but many site even msdn also has working example with imagebutton.

so i tried my best now i am ruin, please help me as i want my site look good.
On Monday, August 13, 2007 3:50:40 PM Imar Spaanjaars said:
Hi Gufran,

You need to look a the final HTML source in the browser. There's a fair chance that the onmouseover attributes are pointing to images that don't exist. For example, when they start with a ~

Imar
On Tuesday, August 14, 2007 8:35:22 AM Gufran Sheikh said:
Hi Imar,
First of all Thanks Thanks Thanks Thanks Thanks Thanks Thanks Thanks
You are great man i tried each n everything but you pick very right point
i.e. "~" now i have also embedded the hover image and made a starimagehover property and used your GetImageUrl method.
what's my silly mistake is in html view source i consentrate on the attributes that attributes are added or not it is added there but the url has this "~".
so thanks and now i am going to implement it with formview or detailsview.
and also there will be some problem if will not do it in right way so i wish you are always here to help my type of people.
again Thanks for quick reply
On Monday, August 27, 2007 12:28:38 PM Prashant said:
Can I put this control in datagrid ?
On Monday, August 27, 2007 7:14:25 PM Imar Spaanjaars said:
Hi Prashant,

Yes you can. See Joshua's remarks below this article.

Imar
On Thursday, August 30, 2007 10:18:44 AM Prashant said:
Hi Imar , Thanks for Quick Response !! Actually I got stucked at following error while putting up rating control in datagrid . I am using this control on  2.0 . Can U Help Me to find out the solution

Method 'Public Sub datalisthandlers(sender As Object, e As System.Web.UI.WebControls.DataListItemEventArgs)' does not have the same signature as delegate 'Delegate Sub DataGridItemEventHandler(sender As Object, e As System.Web.UI.WebControls.DataGridItemEventArgs)'.


Thanks in Advance .

On Thursday, August 30, 2007 10:37:53 AM Imar Spaanjaars said:
Hi Prashant,

Look at the error message you posted. It tells you exactly what the problem is. Hint: it's related to the data controls you are using; not to the Rating Control itself.

Imar
On Thursday, August 30, 2007 11:05:21 AM Prashant said:
hey imar ,

can i have ur email id !! need some more help

Thanks

On Thursday, August 30, 2007 11:38:40 AM Imar Spaanjaars said:
Hi Prashant,

Sorry, I don't do private support by e-mail if it's too off topic from the original article. Please post this on a forum like http://p2p.wrox.com. If you post there, be sure to post loads of extra relevant data that others may need to solve the problem. if you send me the link, I'll try to take a look myself.

Chees,

Imar
On Thursday, September 06, 2007 12:25:02 PM Jay said:
Joshuas example is great to use when you need to display ratings inside ie a repeater, but I can't get it to register new ratings. In your example Imar, the rating ID is hard coded and I can't figure out how to convert the "ContentRating1.DataSource = Values;" inside the "ContentRating1_Rated" to a specific rating that is inside a repeater.
Anyone have any ideas or could give me an example?
On Thursday, September 06, 2007 8:12:57 PM Imar Spaanjaars said:
Hi Jay,

You don't need the DataSource in the Rating event. What you need is the item's ItemId so you know what item was rated. Afterwards, you can rebind the control to its data source.

Imar
On Monday, September 10, 2007 7:25:15 AM ajit said:
Article is very nice this rating control is really rocking but we want to use it within the grid view so can u help us out...........
On Monday, September 10, 2007 8:00:23 AM Imar Spaanjaars said:
Hi ajit,

Please read some of the comments below this article; other people asked the same question.....

Imar
On Thursday, September 13, 2007 6:33:51 AM ajit said:
Hi this starrating is very nice it works with gridview as well....

Now if i want to take input from users for total rating and for total users then how i will modify this code....

Plz help me out.......
On Thursday, September 13, 2007 4:29:58 PM Imar Spaanjaars said:
Hi ajit,

Not sure I understand why you would want to take user input for the total rating and number of stars, or why it would be completely different from what you have done now.

But anyway, this is getting a bit too off-topic to discuss as a comment on this article. May I suggest you post this on p2p.wrox.com?

Cheers,

Imar
On Friday, September 14, 2007 5:49:00 AM ajit said:
Hi sorry to distrub u again

If i m using this control in gridview in multiple rows and i want to update star rating how can i update it ?

Plz help me out ......
On Friday, September 14, 2007 6:10:25 AM Imar Spaanjaars said:
Hi ajit,

Again (and probably for future support requests as well): this is getting a bit too off-topic to discuss as a comment on this article. May I suggest you post this on p2p.wrox.com?

Cheers,

Imar
On Friday, October 26, 2007 11:58:25 PM Dan said:
Hello,

Thanks for the great article.  The test site works perfectly.  However I'm running into a problem with trying to use this control on an existing site.

Instead of using a DLL, I added the code directly to the project.  When I use the control on a page it shows up fine, however as soon as I click a star, the control disappears.  When I step through, I see that in the control's CreateChildControls method ViewState["RateItems"] is no longer set, and this.Page is null.

View state is definitely enabled - I triple-checked that.  This is the first time I've used a CompositeControl, so maybe there's something else I'm missing.  Any help would be appreciated!

Thanks,
Dan
On Saturday, October 27, 2007 8:14:03 AM Imar Spaanjaars said:
Hi Dan,

Not sure what the problem is. What you could try is moving the ViewState mechanism to the DataSource property; e.g. use ViewState as the backing store for the property and see if that helps.

Cheers,

Imar
On Sunday, November 25, 2007 1:39:12 PM Norman said:
I am working on a picture website (vb) and would like to add this cool rating control. I am struggling with how to save the rate data to the database and retreive it on page load. Do I have to create a table with 4 columns? I saw Joshua's VB code but I don't see how the database table looks like? Any comments would be appreciated.

Cheers,

Norman
On Sunday, November 25, 2007 2:18:03 PM Imar Spaanjaars said:
Hi Norman,

Take a look at my comment from  6/4/2007 12:52:45 below this article.

Cheers,

Imar
On Friday, December 07, 2007 12:33:46 PM vgt said:
Hi and thanks for this usefull control...I would like to ask why I can rate more than one time when I refresh my page?Where are the http cookies that tell the system not to allow user to rate again?
Thanks in advance...
On Friday, December 07, 2007 1:40:26 PM vgt said:
I noticed that in the cookie every time there is only one row which apparently means that it saves the info only for the last product the user rated...It does not work if we have many products because every time the same cookie is created with the same name,it erases the last one,so the info for the previous ratings is gone.....what about making some changes to the OnBubbleEvent event?
On Friday, December 07, 2007 6:00:03 PM Imar Spaanjaars said:
Hi vgt,

Are you sure you are setting the ItemId?? Take a look at this code:

myArgs.SupportsCookies = true;
cookieKey = "Rate_" + ItemId.ToString();
cookieValue = ce.CommandArgument.ToString();

As you can see, the cookieKey is a combination of the word Rate, an underscore and the unique ID of the item.

With a unique ItemId - as explained at the beginning of the article - you can use the voting control for multiple content items.

Cheers,

Imar
On Saturday, December 08, 2007 7:43:57 PM vgt said:
The itemId is the guid that is created here right?I call this code in the page load event as it is:

*************************************
if (!IsPostBack)
  {
    ContentRating1.ItemId = Guid.NewGuid();
    ContentRating1.DataSource = Values;
    ContentRating1.DataBind();
  }
*************************************

My problem is in the following code:

***********************************************
protected void ContentRating1_Rating(object sender,
          Spaanjaars.Toolkit.RateEventArgs e)
{
  if (e.HasRated)
  {
    e.Cancel = true;
  }
}
***********************************************
The steps are the following:
1)I go in one page with a product and i rate it,
2)I go in another page with a different product and I rate it(here the http cookie goes different)
3)I return to the first page,and I am able to rate again this product???But I rated it in step 1?I noticed here that the e.Cancel returns false,so it lets me rate it again....

Finally,I have to say that I bypassed the viewstate "Values" since I store the data in a field in my database from which I get them perfectly....However,I do not think that this causes me trouble....

Any suggestion on this would be appreciated,since I loved very much this control....
On Sunday, December 09, 2007 12:11:02 AM Imar Spaanjaars said:
Hi vgt,

The ItemId should be a true, unique ID that *identifies* the object you want to vote for. I only assigned it a Guid for demo purposes. In a real world application you need to assign it a real, steady value. For example, in my own web site, this is the QuickDocId (see query string)

In your case, you keep generating a new Guid everyime you access the page, so the control thinks it's a new item.

Cheers,

Imar
On Sunday, December 09, 2007 10:52:27 AM vgt said:
I think you are right....I need to assign something real, like productId , right?Does it matter if this is an int?
Thanks again...
On Monday, December 10, 2007 6:54:14 AM Imar Spaanjaars said:
Hi vgt,

No it doesn't. It's of type object and inside the class ToString is used....

Imar
On Monday, December 10, 2007 9:26:31 AM vgt said:
Hi Imar,
Today I tried the code we discussed and I put the productId which is real and unique for every product....but the behaviour is the same....
Correct me if I am wrong but in the following code you create again a new cookie if the value key is not found,which means that the previous info about the previous product rating is gone....:

*****************************************
if (!myArgs.Cancel)
        {
          // Create a new cookie value for this rate.
          rateCookie = new HttpCookie("Rate");
          rateCookie.Expires = DateTime.Now.AddMonths(6);
          rateCookie.Values.Add(cookieKey, cookieValue);
          Context.Response.Cookies.Add(rateCookie);
          // Raise the Rated event to notify clients.
          OnRated(myArgs);
          handled = true;
        }
**************************************************
On Monday, December 10, 2007 10:09:30 AM vgt said:
Ok,
Imar I changed the first line of the above code into this:

rateCookie = Context.Request.Cookies["Rate"];

and I rebuild the dll and imported to .net IDE again....
Now it seems to work great.....
Please take a look at this to see if there is a better solution...
Thanks again for this control....
On Monday, December 10, 2007 11:54:08 AM vgt said:
Haaaa,
it's me again....
Well,after the case with the cookie,I would like to make onmouseover event when the mouse is over the stars...Can you tell me where exaclty should I make the changes and if is easy for you,the way to do this????
Many thanks....
Kostas
On Monday, December 10, 2007 1:00:38 PM vgt said:
Well,
It seems today that I am talking alone here....anyway I must add still one change I ve made to the code in ContentRating.cs file so as to check if there is a cookie with such a name otherwise it throws exception because it is not able to find it...If it cannot find it then creates a new one with the same name....:

********************************************
rateCookie = Context.Request.Cookies["Rate"];

        if (rateCookie == null)
        {
            rateCookie = new HttpCookie("Rate");
            Context.Response.Cookies.Add(rateCookie);
        }

        if (!myArgs.Cancel)
        {
          // Create a new cookie value for this rate.
          rateCookie = Context.Request.Cookies["Rate"];
          rateCookie.Expires = DateTime.Now.AddMonths(6);
          rateCookie.Values.Add(cookieKey, cookieValue);
          Context.Response.Cookies.Add(rateCookie);
          // Raise the Rated event to notify clients.
          OnRated(myArgs);
          handled = true;
        }
*************************************************
On Monday, December 10, 2007 2:02:42 PM Siva Pittu said:
Please could anyone help me in using this control in a datalist control. I have to use this control in a project. Could any share the source code they have of using this control in a datalist.

Thanks and Regards.
On Monday, December 10, 2007 9:21:43 PM Imar Spaanjaars said:
Hi vgt,

Yes, talking on your own today ;-) I've been quite busy, so I didn't have the time to respond.

If you have any further questions, like implementing onmouseover images, may I suggest you post them on a forum like p2p.wrox.com? Unfortunately, I don't have the time to dig into this for you, but there may be others there that can help.

Cheers,

Imar
On Monday, December 10, 2007 9:28:23 PM Imar Spaanjaars said:
Hi vgt,

One more thing. take a look at this:

myArgs.SupportsCookies = true;

cookieKey = "Rate_" + ItemId.ToString();
cookieValue = ce.CommandArgument.ToString();

rateCookie = Context.Request.Cookies["Rate"];

if (rateCookie == null)
{
  myArgs.HasRated = false;
}
else
{
  if (rateCookie.Values[cookieKey] == null)
  {
    myArgs.HasRated = false;
  }
  else
  {
    myArgs.HasRated = true;
  }
}

Notice how I retrieve the cookie in a variable called rateCookie. I retrieve the cookie by its name: Rate. However, this is a *multi valued cookie*, which means I can store multiple values under the same name. So, there is only one cookie, that contains values for each ItemId you voted for.

Maybe this is where your confusion comes from? AFAIK, the control should work as advertised and only allow you to vote for an item once (provided you don't delete the cookies).

Hope this clears up some confusion.

Imar
On Monday, December 10, 2007 9:29:28 PM Imar Spaanjaars said:
Hi Siva Pittu,

Please read the comments on this post; others have asked the same question.

Cheers,

Imar
On Monday, December 10, 2007 10:04:33 PM Imar Spaanjaars said:
Hi vgt,

Never mind my previous message. Had another look and you are right. When checking, I take multi values into account. When setting, I don't, and thus overwrite previous votes.

Will try to update the code but that may take some time....

Imar
On Tuesday, December 11, 2007 5:01:15 AM Siva Pittu said:
Hi Imar,

I was trying out that to implement in my project and i had a problem in creating the control in the datalist. So please let me know whether i could get source code to use this control in datalist.

Any help is appreciated.

Thanks and Regards
Siva Pittu
On Tuesday, December 11, 2007 7:53:09 AM Imar Spaanjaars said:
Hi Siva Pittu,

I can't "get you the source code" as I don't have any ready-made examples.

However, it isn't that hard. Just put the control in a DataList and then handle the various events as described earlier in the posts to this article.

If you need more help, please consult a forum like p2p.wrox.com.

Cheers,

Imar
On Wednesday, December 19, 2007 3:19:46 AM cheing said:
very good good thx for share
On Tuesday, January 01, 2008 2:41:56 PM Satheesh babu said:
Hi Imar,

Thank you so much!!

I am going to implement this on my site...

Thanks again!!!you saved my time like anything!!!!

Good work buddy!!!

Regards,
Satheesh
On Wednesday, January 16, 2008 11:26:07 PM Andy said:
I just cant understand how to store the rating in the database.
I have the columns, but where do i put the SQL UPDATE query?
On Wednesday, January 16, 2008 11:30:59 PM Imar Spaanjaars said:
Hi Andy,

You should write custom code that performs the update and call it from the control's OnRated event.

Cheers,

Imar
On Thursday, January 17, 2008 12:19:06 AM Andy said:
i cant find the OnRated event anywhere?

And can you please show an example of the query?

many thanks.
Sorry, but im just an asp.net beginner.
On Thursday, January 17, 2008 12:20:36 AM Andy said:
You dont mean to put the query between:
protected void ContentRating1_Rated(object sender,
           Spaanjaars.Toolkit.RateEventArgs e)
{
  
  // Query
}

right?
On Thursday, January 17, 2008 8:02:55 AM Imar Spaanjaars said:
Hi Andy,

WHy not? That's exactly the right location.....

Imar
On Wednesday, February 27, 2008 1:36:38 PM Roel Alblas said:
Tryin to convert it to VB. Not working :-( Can you help me?

Thanks!

Roel

<snip>Code removed by Imar</snip>
On Wednesday, February 27, 2008 1:45:05 PM Imar Spaanjaars said:
Hi Roel,

First of all "Not working" is a very bad problem description. How do you expect me to provide you with a solution if you don't even say what the problem is?

Secondly, I am not your on-line code converter or debugger, even if it is about one of my articles. Maybe you can try one of the on-line code converters available? And if they don't convert correctly, maybe you can post follow up questions on a forum like p2p.wrox.com?

Cheers,

Imar
On Friday, March 28, 2008 9:27:29 AM Kees said:
Hi Imar,

I'm kind of new to programming in C# for asp.net. Am developing a website with a rating. adding rates is now by a form and radiobuttonlists etx. It all works and it now gives all the right ratings in figures from 0 to 5 already, but just want to display it nice and visual in starts. So I won't need much of this code as in the rating but more in visualising the figures in star images...
Do you have some advise to use some of this code or something else?

thanks for the good page, helps!
On Friday, March 28, 2008 10:37:03 PM Imar Spaanjaars said:
Hi Kees,

No, I don't have much advice, as I don't understand what you are asking. You want a rating without rating? Isn't that just a matter of removing the code that lets you rate? And just keep the part that displays the images??

Imar
On Friday, July 04, 2008 5:38:40 AM Sandeep Prajapati said:
hi,

you have done very nice job..

I want to integrate this control with my database....

As you told...

i have made a table in database with fields-- Id,Rate[1..5]

now i want to bind the control with database..

initially it shld display me current rates from database and after updating  the new rate it shld display...

can u please give me some hint ..how to bind the control with my database

Thanx
~Sandeep
On Friday, July 04, 2008 7:57:19 PM Imar Spaanjaars said:
Hi Sandeep,

Check out my comment to Ryan on 2/26/2007 10:25:28 PM.

To get the data, use standard ADO.NET code. You could get the data in an array, a DataSet or whatever makes sense for you. Up to you...

Imar
On Saturday, July 05, 2008 3:45:53 PM Iulian said:
Hi,

Very cool control but I have a problem when I set the images urls in the aspx file to something. If I set either one of the star images to something else, the BubbleEvent doesn't get called for some reason... No events are raised when you click the star and the page simple posts back and the control disappears (because it is no longer bound because the OnRated never gets called...) Any idea?

Thanks!!
On Sunday, July 06, 2008 10:27:37 AM Imar Spaanjaars said:
Hi Iulian,

It's been a while since I looked at this code, but I can hardly imagine that's the real cause of the problem. The image is determined by the method GetImageUrl which simply gets a local URL or one to refer to an embedded image. All the other code is the same and as such shouldn't cause the behavior you describe.

Have you tried debugging the control?

Imar
On Thursday, August 28, 2008 4:01:29 AM Soni.S.Raj said:
You r great........
Its realy super..........
But i have a problem using this control in a gridview .Can you send a sample code using this control inside a gridview..
Its really helpful for me.

Thanking you.
On Thursday, August 28, 2008 6:15:05 AM Imar Spaanjaars said:
Hi Soni,

Sorry, no complete code available. However, it shouldn't be too hard. You should be able to bind using the DataSource attribute in an item template, or in code behind in one of the GridView's events.

Please post a follow up question on a forum like http://p2p.wrox.com if you need more help with this; it's not so easy to share code through these comments.

Imar
On Thursday, August 28, 2008 8:05:14 AM Soni.S.Raj said:
Thanks

I know how to bind a gridview in template field ,i am testing with a dropdownlist its working fine. But the problem using this control. I need only the code behind of these control for gridview rowbound event.........

Thanking you.
On Thursday, August 28, 2008 8:41:35 AM Imar Spaanjaars said:
Hi Soni,

All you need to do is find a reference to the control using FindControl, set the DataSource property and call DataBind:

myContentRating.ItemId = YourItem;
myContentRating.DataSource = YourValues;
myContentRating.DataBind();

For further technical questions, can you please post this on a forum like http://p2p.wrox.com.

Imar
On Sunday, September 07, 2008 1:59:21 PM Anuj kurchania said:
i used it in vb .net .
convert your c# code in vb.
but it give an error
"contentRating_rating is not a member of asp.net"
please help me
On Sunday, September 07, 2008 2:06:56 PM Imar Spaanjaars said:
Hi Anuj,

Hard to tell without seeing your code. Would you mind posting this on p2p.wrox.com and send me the link?

Cheers,

Imar
On Monday, September 22, 2008 1:28:21 AM mark said:
Thanks for creating and posting this. Very much appreciated.
On Friday, November 14, 2008 11:01:40 AM Shabana said:
nice work... increased my appetite to learn and create more custom control..

Thnxs a lot!!!
On Monday, November 24, 2008 6:32:51 PM Rita said:
Imar,

Thanks for this wonderful article. But can you please tell me if at all only registered user can rate an project means how should i design an database?
On Saturday, December 13, 2008 10:35:45 AM asa said:
Thanks for creating and posting this. Very much appreciated.
On Monday, May 18, 2009 1:25:39 PM asdf said:
Good.
On Thursday, May 21, 2009 9:37:48 AM kodi said:
Can you plase show us how to integrate this control with the database. It would make this article more complete. Thanks For the article. It was really helpful.
On Sunday, May 24, 2009 10:12:03 AM Imar Spaanjaars said:
Hi kodi,

Please take a look at the link in the "Update section" at the top of this article.

Cheers,

Imar
On Sunday, May 24, 2009 5:07:04 PM Kodi said:
Hi Imar,

Thanks a lot for your article. This is a great article. An article so badly needed. :)

Thanks again!!!!
On Friday, June 19, 2009 5:39:57 AM keyur said:
I want to know that will it allow to give half star rating?
i.e. can i able to click on half star? so i get that user input half star for that user?

Thanks
On Saturday, June 20, 2009 9:52:49 AM Imar Spaanjaars said:
Hi keyur,

Nope, that's not supported. Shouldn't be too hard to build that in yourself though...

Imar
On Tuesday, August 04, 2009 5:05:56 PM Dulcie said:
I refer to 'On Sunday 9/7/2008 3:59:21 PM Anuj kurchania' post.
How can I convert your sample coding into .aspx file?
On Wednesday, August 05, 2009 11:42:15 AM Imar Spaanjaars said:
Hi Dulcie,

I don't understand what you're asking (I already didn't understand what Dulcie was asking). Why do you want to convert a server control to an ASPX page?

Imar
On Saturday, August 22, 2009 8:55:40 PM Benn said:
Hi Imar,

My english is not very good, I will try to explain as clear as i can.

I used this control in a datalist (control is in an itemtemplate) and the code posted by Joshua starr with some modification:

[asp:DataList ID="dlResto" DataKeyField="IdResto"  OnItemCreated="datalisthandlers"  RepeatColumns="1" RepeatDirection="Vertical" runat="server" ]
[ItemTemplate]
  [table]
   [tr]
    [td ]
     [b][%#Eval("Nom")%][/b]  
     [%#Eval("Description")%]
    [/td]
   [/tr]
    [td align ="right" valign="bottom"]
     [isp:ContentRating ID="RestoRating"  runat="server" OnRated ="RestoRating_Rated"  OnRating="RestoRating_Rating"   LegendText="{0} rates / {1} avg."  ]
     [/isp:ContentRating]
    [/td]
   [/tr]
  [/table]
[/ItemTemplate]
[/asp:DataList]


    protected void datalisthandlers(object sender, DataListItemEventArgs e)
    {
        //Rating Control Handler
        if((e.Item.ItemType == ListItemType.Item) || (e.Item.ItemType == ListItemType.AlternatingItem))
        {
            if(e.Item.FindControl("RestoRating") != null)
            {
                ContentRating RestoRat = new ContentRating();
                RestoRat = (ContentRating) (e.Item.FindControl("RestoRating"));
                int[] arValues;
                Functions fct = new Functions();
                arValues = fct.GetRate(int.Parse(dlResto.DataKeys[e.Item.ItemIndex].ToString()));// e.Item.ItemIndex);
                //arValues = new int[] {0, 0, 5, 5, 5};
                RestoRat.ItemId = dlResto.DataKeys[e.Item.ItemIndex];
                RestoRat.DataSource = arValues;
            }            
        }
        //dlResto.DataBind();
    }

The rates are correctly displayed, but when I try to rate an item by clicking on one star, the control disappears after postback, and the evens RestoRating_Rated and RestoRating_Rating are not fired.

Can you please help me fix these issues?

Thanks a lot.
On Sunday, August 23, 2009 8:02:30 AM Imar Spaanjaars said:
Hi Benn,

Sounds like you have an issue with binding the DataList and its internal voting controls. Maybe you bind too often; or not often enough. Difficult to say without seeing more of your code in a more readable format.

May I suggest you post your code in an appropriate forum at http://p2p.wrox.com/index.php?referrerid=385? Much easier to share code there.

Cheers,

Imar
On Sunday, August 23, 2009 8:35:06 PM Benn said:
Hi Imar,

I've posted it in that forum, here is the link:

http://p2p.wrox.com/asp-net-3-5-professionals/75842-contentrating-used-datalist.html#post245712

Tks.
On Monday, October 05, 2009 7:41:47 AM Thamil said:
Excellent job you done, its working fine, great. It’s very useful to all webmasters.

-Thamil.
On Wednesday, June 09, 2010 12:55:22 PM Chirag Davda said:
Thanks keep it up :)
On Tuesday, June 15, 2010 3:44:27 AM anjan said:
The values are not saved in the database?
Database not required? So how is the rating value saved then?
On Tuesday, June 15, 2010 8:43:07 AM Imar Spaanjaars said:
Hi anjan,

You need to do that yourself in the Rating event raised by the control as this is application specific and depends on your data model.

Check out the updated article (see beginning of this one) for a sample implementation.

Cheers,

Imar
On Saturday, September 11, 2010 2:02:02 PM Nitin Sawant said:
Thank you
On Wednesday, November 10, 2010 6:32:09 AM ajunsj said:
Dear sir:
   Could you send the code to me about this articles?
  

Thanks!
On Wednesday, November 10, 2010 6:38:49 AM Imar Spaanjaars said:
Hi ajunsj,

Send you teh codez? I think you're better off with a pair of glasses.... I mentioned how to get the source in the article a few times.

Cheers,

Imar
On Monday, January 31, 2011 12:40:31 PM atzmon said:
hi,
its seem not working inside datalist
is it true or problem in my site?
when its not inside DATALIST its work great
On Monday, January 31, 2011 12:48:02 PM Imar Spaanjaars said:
Hi atzmon,

Search the comments for this article for DataList and you'll find a couple of suggestions, including a link to a post on the Wrox P2P forums that provides more information.

Cheers,

Imar
On Wednesday, June 29, 2011 4:24:03 AM jayashree said:
hi..
i m trying to place contentrating control in itemtemplate of repeater..
bt i m unable to place..itz giving error..

can we place this control in repeater
On Wednesday, June 29, 2011 7:02:29 AM Imar Spaanjaars said:
Hi jayashree,

Follow the link to the new article in the Update section at the start of this article, and then search for Repeater.

Cheers,

Imar
On Friday, July 01, 2011 10:09:15 PM Jose J said:
thanks for sharing the article... i am having a strange problem after much debug my code thought to ask you here...

the problem i am having is rendering, at runtime i dont see the control is rendered and i check the source code it renders only "[table id="ContentRatingControl1" style="height:16px;width:140px;"] [/table]" no star nothing. and when i see the page at design time i able to see the control with stars.

i place the debug on CreateControlHierarchy method but it never goes there...

any help?

Thanks.
On Saturday, July 02, 2011 2:19:33 AM Imar Spaanjaars said:
Hi Jose,

Maybe you're not binding any data to the control? Do you see the same behavior with my control's code and sample site? Or did you make changes to the code that may cause this issue?

Imar
On Tuesday, July 05, 2011 7:35:33 AM Jose J said:
that was the case i was not binding any data control - thanks

i have another question, can 5 star rating be used with
this table structure something like this?

ContentId       int
MinStarRating  int
MaxStarRating int

will that work with your control?
On Friday, July 08, 2011 3:35:38 PM Jose J said:
@Joshua Starr

can you help me i am getting the same message "Can't bind a datasource without a valid ItemId " no matter what...

if you email me (dsmportal at yahoo dot com ) i can post my code.

hope to hear from you - Thanks.
On Friday, July 08, 2011 8:14:24 PM John said:
i able to display the rating star control in the gridview but i am having problem when i click on the star to rate as soon as i click the star disappears after it postback and i know i am not binding onRated event but my scanrio is different the sample file you have in the project or repater project sample does not show how would you take care of it in gridview or repeater control.

how would you achieve in grdiview or repeater? like when you click on the star to rate and bind ? any clue?

Thanks.
On Saturday, July 09, 2011 3:57:37 AM Imar Spaanjaars said:
Hi John,

Follow the link to the new article in the Update section at the start of this article, and then search for Repeater.

Cheers,

Imar
On Saturday, July 09, 2011 7:33:47 PM John said:
Hi Imar,

thanks for your reply., i see the repeater example but when i am trying to do the same thing in Gridview but did not work (When I use the control on a page it shows up fine, however as soon as I click a star, the control disappears)

i have a created super simple one page project with dummy data, i have upload here http://www.abumisbah.com/ContentRating.rar

would you please have a look? i have spend good amount of time in it trying to figure out but no luck.

appreciate any help.

Thanks again.
On Monday, July 11, 2011 2:12:01 PM Imar Spaanjaars said:
Hi John,


Hmmm, not sure what, but there appears to be something funky with the embedded image. Two of them work, but OneStarDisabled.gif seems to be causing issues.

If I add the following to the control in markup:

HalfStarImage="HalfStar.gif" OneStarImage="OneStar.gif" OneStarImageDisabled="OneStarDisabled.gif"

and copy the three files to the root o the site, and add an additional call to RestoRat.DataBind() at the end of OnRowCreated, it seems to work much better.

Not sure why the image doesn't get embedded though (unfortunately, no time to look into it).

Hope this helps.

Cheers,

Imar

Talk Back! Comment on Imar.Spaanjaars.Com

I am interested in what you have to say about this article. Feel free to post any comments, remarks or questions you may have about this article. The Talk Back feature is not meant for technical questions that are not directly related to this article. So, a post like "Hey, can you tell me how I can upload files to a MySQL database in PHP?" is likely to be removed. Also spam and unrealistic job offers will be deleted immediately.

When you post a comment, you have to provide your name and the comment. Your e-mail address is optional and you only need to provide it if you want me to contact you. It will not be displayed along with your comment. I got sick and tired of the comment spam I was receiving, so I have protected this page with a simple calculation exercise. This means that if you want to leave a comment, you'll need to complete the calculation before you hit the Post Comment button.

If you want to object to a comment made by another visitor, be sure to contact me and I'll look into it ASAP. Don't forget to mention the page link, or the QuickDocId of the document.

For more information about the Talk Back feature, check out this news item.