Creating an ASP.NET 2.0 Polling User Control: Building the Polling User ControlBy Scott Mitchell
Welcome to the second article in a three-part series on building a polling User Control. In the first installment, Design Decisions and the Data Model, we looked at the design decisions made prior to creating the control as well as the poll's data model, which consisted of three tables in a SQL Server 2005 database.
We are now ready to start building the poll's user interface. Since my poll control only allows authenticated users to vote, if an anonymous visitor reaches the poll or if the authenticated user has already voted then the User Control displays the results of the poll in a read-only interface. If visited by an authenticated user who has yet to take the poll, it allows the user to vote, after which the results are stored in the database and the results displayed to the user.
In addition to building the polling User Control, we'll also look at how to add the poll to a web page. Read on to learn more!
Enumerating the Different Pieces of the Polling User Control
The typical polling user interface includes some question text, like "What is your favorite programming language?", along with a series of radio buttons listing possible answers, like "C#", "Visual Basic", "Pascal", and so forth. The visitor can select an answer and then cast his vote. After voting, the interface changes to show the current voting breakdown per answer. Therefore our User Control needs two separate displays: one that shows the poll answers and allows voting; and one that shows the answers and the voting breakdown, but prohibits voting.
When the voting interface is displayed, each poll answer needs to be rendered as a radio button option; there also needs to be a button that, when clicked, casts the user's vote. On the other hand, when the read-only interface is shown, each poll answer needs to list how many total people voted for that option along with the percentage of votes. In addition to the total numbers and percentage, my polling User Control provides a line depicting the percentage of votes. For example, if a particular answer has received 40% of the votes, a line underneath it will extend to 40% of the width of the User Control.
Regardless of whether the read-only or voting interface is displayed, the poll's question needs to be displayed at the top of the User Control.
The challenges facing us, then, are thus:
- Create the user interface elements - we will use a variety of data Web controls to build our user interface, including a FormView, DataList, and RadioButtonList. There will also be Label and Button controls present.
- Allow the page developer to specify the poll to display - a single instance of the poll User Control displays
a poll specified by a
PollIDproperty in the User Control's code-behind class.
- Insert a record into the
UserResponsestable when a vote is cast - the particular answer plus the ID of the currently logged on user must be inserted into a new row in
- Display the poll results in the read-only interface - this involves determining the total number of votes cast for the poll and calculating and displaying the percentages both as text and as a line.
- Determine when to show the read-only interaface vs. the voting interface - we need to add logic that can ascertain whether to show the current user the voting interface or the read-only interface. As discussed in Design Decisions and the Data Model, only authenticated users who have not yet taken the poll should see the voting interface; all other visitors will see the read-only interface.
Creating the User Interface Elements
Since the User Control needs to display information about the specified poll, I started by adding a FormView to the page and binding it to a SqlDataSource that returns all of the fields from the
Pollstable for the specified poll using the following
The SqlDataSource is a control new to ASP.NET 2.0 that makes it a breeze to work with data. I used the two SqlDataSource controls in the poll User Control and a few others in the administration pages, as we'll discuss in the third installment. For more information on working with data in ASP.NET 2.0, check out my multi-part article series: Accessing and Updating Data in ASP.NET 2.0.
After configuring the SqlDataSource control and binding it to the FormView, I edited the FormView's
ItemTemplate so that it displayed just the selected poll's
Next, I needed to add controls within the FormView's
ItemTemplate to display either the read-only or
voting interface. To separate these two interfaces, I first added two Panel controls to the
the Label displaying the poll's question. I (arbitrarily) decided to create the voting interface in the first Panel and
added to it a RadioButtonList and Button control. I bound the RadioButtonList control to a new SqlDataSource control
PollAnswersDataSource that includes
SelectCommand returns all of the answers
for the poll being displayed and sorts them by the
SortOrder column in ascending order:
InsertCommand will be executed when the Button is clicked. This will require writing a bit of code,
but we'll get to that later in this article. The
InsertCommand adds a new record to the
table, indicating the answer that the currently logged on user voted:
Finally, in the second Panel, which displays the read-only interface, I added a DataList control and bound it to a SqlDataSource
PollResultsDataSource that returns a record for each poll answer along with the total number of votes received for the answer. This total vote
number was obtained by joining the
UserResponses and using a
aggregate function to determine the number of votes for each answer, as the following query shows:
For more information on aggregate functions in SQL (like
COUNT) and using the
GROUP BY clause, see
Group By Clause.
Specifying the Poll to Display
Since our poll database could include numerous polls, we need a way to let the page developer specify what poll to display in the polling User Control. While we could just show the most-recently added poll or use some sort of date scheme, this would prohibit scenarios where we might want to show several different polls on a website. For maximum flexibility, my polling User Control requires that the developer specify the poll to display. Since each poll is uniquely identified in the database via its
PollIDvalue, this is an ideal way to specify what poll to show in the User Control.
I added a public property called
PollID to the User Control and had its value persisted in view state (so
that it would be remembered across postbacks if it was assigned programmatically):
Each of the SqlDataSources in User Control include a
@PollID value that is used in the
to return poll details or answers for the specified poll. In order to have the User Control's
value used as these parameter values, I needed to create event handlers for the SqlDataSource controls'
events and set the parameter value using code like so:
Inserting a Record Into
UserResponses When a Vote is Cast
As mentioned earlier, the SqlDataSource used by the voting interface includes an
InsertCommandthat, when executed, will add a row to the
UserResponsestable. In order for this insert to occur, we need to call the SqlDataSource's
Insert()method; we want to do this when the "Vote" Button is clicked. To accomplish this I created an event handler for the Button's
Clickevent using the following code:
PollAnswersDataSource SqlDataSource control is within the FormView's
we have to use the
FindControl("controlID") method to obtain a reference to the control. Once we
have that reference we can simply call the
Insert() method. After doing so, we want to update the display
so that the user sees the read-only interface (since they just voted). Moreover, we want to update the read-only interface
to include the user's vote (as well as any votes that may have occurred between the time the user loaded up the
page and submitted her vote). To accomplish this, all we need to do is have the FormView rebind its data. This will
requery the database and reevaluate whether the user should see the voting or read-only interface - hence the
call to the FormView's
DataBind method is all that is needed!
Displaying the Poll Results in the Read-Only Interface
Displaying the list of poll answers in the voting interface is simple: the RadioButtonList renders the appropriate interface for use based on the records returned from the
PollAnswersDataSourceSqlDataSource. The read-only interface is a bit more challenging. The
PollResultsDataSourceSqlDataSource returns a record for each answer in the poll along with the total number of votes made for that answer. However, in order to display the percentage of votes each answer has received, we need to know the total number of votes made for this poll. This is accomplished by programmatically connecting to the database and running the following query:
Once we have this information we can properly display the percentage next to each answer, since it's merely the number of
votes for that answer divided by the total number of votes. We can make this calculation in the DataList's
event handler, which is raised as each item in the DataList is bound to data. For this to all work, it is imperative that
we have the total number of votes before the
ItemDataBound event fires for the first time.
This is accomplished by putting the above SQL query and associated logic in an event handler for the DataList's
DataBinding event fires before the
ItemDataBound events. Here is the code used in the
DataBinding event handler:
The total number of votes is calculated and stored in the page variable
is a Label in the read-only interface that shows the total number of votes.
ItemDataBound event handler each answer's total number of votes is retrieved and the percentage is
calculated. The percentage is then displayed both textually (through the
PercentageLabel Label) and graphically
by specifying the width of the
PercentageImage image control as the percentage of votes. This image width
setting stretches (or shrinks) the line image, which can be found at
The net result is that each answers total number of votes and percentage are shown, as the following screen shot illustrates:
Determining When to Show the Read-Only Interface vs. the Voting Interface
The last piece of the puzzle here is determining when the read-only interface should be shown versus when the voting interface should be shown. The polling User Control should automatically be able to make this determination without requiring any code or property settings from the web page that contains the User Control. As discussed in Design Decisions and the Data Model, only authenticated users who have not yet taken the poll should see the voting interface; all other visitors will see the read-only interface.
When designing this control I wanted to make it easy for a reader to change this poll display logic. Therefore,
I put the logic in a User Control method named
CanUserTakePoll(), which returns a Boolean value indicating
whether the current visitor can or cannot take the poll. If you want to alter the poll taking criteria, all you need
to do is update this method with your own custom logic. (Note: you will likely also need to add additional
logic when the vote is cast, somehow noting that the current visitor has just made a vote so that she can see the results
and to reduce the likelihood of ballot stuffing.)
My implementation of
CanUserTakePoll() returns False if the visitor is anonymous or if the user has already
taken the poll:
CanUserTakePoll() method is called from the FormView's
DataBound event handler, which uses this
value to determine what Panel will be visible.
Using the Polling User Control in an ASP.NET Web Page
The download available at the end of this article provides a working demo of the polling User Control and administration pages. You can practice with adding the polling User Control to a new page via this demo. Start by adding a new web page. Next, drag the
Poll.ascxUser Control from the Solution Explorer onto the page's Design surface. This will add the User Control and the appropriate
@Registerdirective to the page.
All that remains at this point is to specify the poll to display. This can be done declaratively by setting the
in the Properties window. Alternatively, this property can be set programmatically. The
in the demo application shows how to set
PollID programmatically. It includes a DropDownList that lists all of
the polls in the database. Upon selecting a new poll, the User Control's
PollID value is set and the polling
User Control is updated. If the user is not logged in or if they've already taken the poll then they'll see the read-only
interface. If they are signed in and have yet to take that poll, its voting interface will be displayed.
At this point we have looked at the design goals of the polling User Control, its data model, and how the control was constructed. And, at this point, all of the essential functionality is complete. However, it would be nice to add web-based administration pages to enable admins to add or edit polls and view the results of existing polls. I have created such a set of web pages; they are the topic for the third and final installment!
Until then... Happy Programming!