You want to create a simple bar chart on the fly without having to resort to a commercial package to do so.
Use a combination of data binding with a Repeater
control
and the well-known HTML trick of stretching an image to create the
bars.
In the .aspx
file, add a
Repeater
control with an
ItemTemplate
.
In the code-behind class for the page, use the .NET language of your choice to:
Assign the data source to the Repeater
control and
bind it.
In the ItemDataBound
event handler that is called
for each item in the Repeater
, set the width of
the bar in the passed Repeater
row.
Figure 12-2 shows some typical output. Examples
Example 12-7 through Example 12-9
show the .aspx
file and VB and C# code-behind
files for an application that implements this solution.
This recipe provides a rather simple approach that combines data binding and HTML tricks to create a bar graph with very little coding, and without the need to purchase any additional components. By using more complex HTML, you can add more labels and other enhancements to this recipe, which may make it more useful for your situation.
The example we use to illustrate this solution generates a bar chart from chapter and problem data in a database. (The source of the data is not that important. Rather, it’s the technique for generating the graph on the fly that is the main focus of this recipe.) The bar chart is created from an HTML table, with the top row used to label the chart.
This recipe advocates using a Repeater
control to
generate the rows in a table that represent the bars on the chart.
The rows generated by the Repeater
are defined in
the ItemTemplate
element, which in our example
contains two columns. The first column is used to output the chapter
number. In our example, the chapter number is obtained by binding the
cell text to the Chapter column in the data source.
The second column contains the bar representing the number of problems in the chapter. The bar is created by using an HTML image tag with the source set to a 1-pixel-by-1-pixel image. The height and width attributes of the image “stretch” the image to the size of the bar needed to represent the number of problems in the chapter. In our example, the height is set to a fixed value of 15 pixels, but the width is adjusted to represent the number of problems in the chapter. The width is adjusted in the code-behind and is discussed later.
The second column also contains a label to indicate the actual number
of problems in the chapter. The number of problems in a chapter is
obtained by binding the cell text to the
ProblemCount
column in the data source. This label
is placed at the end of the bar with a nonbreaking space
(
) to separate the label from the end of
the bar.
The Page_Load
method in the example code-behind
reads the data from the database and then binds the data to the
Repeater
control on the page.
The code-behind class also implements the
ItemDataBound
event handler to provide the ability
to adjust the width of the image used for the bar. The
ItemDataBound
event fires once per row in the
Repeater
as the row is data bound. In this event
we need to get a reference to the HTML image in the row using the
FindControl
method of the row and then set the
width of the image to reflect the number of problems in the chapter
represented by the row.
If this recipe does not provide the richness you need for your chart,
you can create an image using the concepts presented in Recipe 12.1. The System.Drawing
classes provide all of the functionality to create very sophisticated
charts using the GDI+. They do require considerably more coding,
however, as you must build your graphs from the ground up using the
basic ingredients of pen, brush, point, rectangle, and the like.
Recipe 12.1; MSDN Help for more
information on the System.Drawing
class
Example 12-7. Create bar chart dynamically (.aspx/.vb)
<%@ Page Language="vb" AutoEventWireup="false" Codebehind="CH12CreateChartVB.aspx.vb" Inherits="ASPNetCookbook.VBExamples.CH12CreateChartVB" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>Create Chart</title> <link rel="stylesheet" href="css/ASPNetCookbook.css"> </head> <body leftmargin="0" marginheight="0" marginwidth="0" topmargin="0"> <form id="frmTestDynamicChart" method="post" runat="server"> <table width="100%" cellpadding="0" cellspacing="0" border="0"> <tr> <td align="center"> <img src="images/ASPNETCookbookHeading_blue.gif"> </td> </tr> <tr> <td class="dividerLine"> <img src="images/spacer.gif" height="6" border="0"></td> </tr> </table> <table width="90%" align="center" border="0"> <tr> <td align="center"> </td> </tr> <tr> <td align="center" class="PageHeading"> Dynamic Chart Creation (VB) </td> </tr> <tr> <td><img src="images/spacer.gif" height="10" border="0"></td> </tr> <tr> <td align="center"> <table border="2" bordercolor="#000080" cellpadding="10"> <tr> <td> <table cellpadding="0" cellspacing="0" border="0"> <tr> <td align="center" colspan="2" class="SubHeading"> Problems Per Chapter<br /><br /></td> </tr><asp:Repeater ID="repChartBar" Runat="server">
<ItemTemplate>
<tr>
<td class="LabelText">
<%# DataBinder.Eval(Container.DataItem, "Chapter") %>
</td>
<td class="LabelText">
<img id="imgChartBar" runat="server"
src="images/blueSpacer.gif" border="0"
Height="15" align="middle" >
[<%# DataBinder.Eval(Container.DataItem, "ProblemCount") %>]
</td>
</tr>
</ItemTemplate>
</asp:Repeater>
</table> </td> </tr> </table> </td> </tr> </table> </form> </body> </html>
Example 12-8. Create bar chart dynamically code-behind (.vb)
Option Explicit On Option Strict On '----------------------------------------------------------------------------- ' ' Module Name: CH12CreateChartVB.aspx.vb ' ' Description: This module provides the code behind for the ' CH12CreateChartVB.aspx page. ' '***************************************************************************** Imports Microsoft.VisualBasic Imports System Imports System.Configuration Imports System.Data Imports System.Data.Common Imports System.Data.OleDb Namespace ASPNetCookbook.VBExamples Public Class CH12CreateChartVB Inherits System.Web.UI.Page 'controls on the form Protected WithEvents repChartBar As System.Web.UI.WebControls.Repeater '*************************************************************************** ' ' ROUTINE: Page_Load ' ' DESCRIPTION: This routine provides the event handler for the page load ' event. It is responsible for initializing the controls ' on the page. '--------------------------------------------------------------------------- Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load Dim dbConn As OleDbConnection Dim dc As OleDbCommand Dim dr As OleDbDataReader Dim strConnection As String Dim strSQL As String If (Not Page.IsPostBack) Then Try'get the connection string from web.config and open a connection
'to the database
strConnection = _
ConfigurationSettings.AppSettings("dbConnectionString")
dbConn = New OleDb.OleDbConnection(strConnection)
dbConn.Open( )
'build the query string and get the data from the database
strSQL = "SELECT DISTINCT ChapterID AS Chapter, " & _
"count(*) AS ProblemCount " & _
"FROM Problem " & _
"GROUP BY ChapterID"
dc = New OleDbCommand(strSQL, dbConn)
dr = dc.ExecuteReader( )
'set the source of the data for the repeater control and bind it
repChartBar.DataSource = dr
repChartBar.DataBind( )
Finally 'cleanup If (Not IsNothing(dbConn)) Then dbConn.Close( ) End If End Try End If End Sub '*************************************************************************** ' ' ROUTINE: repChartBar_ItemDataBound ' ' DESCRIPTION: This routine is the event handler that is called for each ' item in the datagrid after a data bind occurs. It is ' responsible for setting the width of the bar in the ' passed repeater row to reflect the number of problems in ' the chapter the row represents '---------------------------------------------------------------------------Private Sub repChartBar_ItemDataBound(ByVal sender As Object, _
ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs) _
Handles repChartBar.ItemDataBound
Dim img As System.Web.UI.HtmlControls.HtmlImage
'get a reference to the image used for the bar in the row
img = CType(e.Item.FindControl("imgChartBar"), _
System.Web.UI.HtmlControls.HtmlImage)
'set the width to the number of problems in the chapter for this row
'multiplied by a constant to stretch the bar a bit more
img.Width = _
CInt(CType(e.Item.DataItem, DbDataRecord)("ProblemCount")) * 10
End Sub 'repChartBar_ItemDataBound
End Class 'CH12CreateChartVB End Namespace
Example 12-9. Create bar chart dynamically code-behind (.cs)
//---------------------------------------------------------------------------- // // Module Name: CH12CreateChartCS.aspx.cs // // Description: This module provides the code behind for the // CH12CreateChartCS.aspx page // //**************************************************************************** using System; using System.Configuration; using System.Data; using System.Data.Common; using System.Data.OleDb; using System.Web.UI.WebControls; namespace ASPNetCookbook.CSExamples { public class CH12CreateChartCS : System.Web.UI.Page { // controls on the form protected System.Web.UI.WebControls.Repeater repChartBar; //************************************************************************ // // ROUTINE: Page_Load // // DESCRIPTION: This routine provides the event handler for the page // load event. It is responsible for initializing the // controls on the page. //------------------------------------------------------------------------ private void Page_Load(object sender, System.EventArgs e) { OleDbConnection dbConn = null; OleDbCommand dc = null; OleDbDataReader dr = null; string strConnection = null; String strSQL = null; // bind the item data bound event this.repChartBar.ItemDataBound += new RepeaterItemEventHandler(this.repChartBar_ItemDataBound); if (!Page.IsPostBack) { try {// get the connection string from web.config and open a connection
// to the database
strConnection =
ConfigurationSettings.AppSettings["dbConnectionString"];
dbConn = new OleDbConnection(strConnection);
dbConn.Open( );
// build the query string and get the data from the database
strSQL = "SELECT DISTINCT ChapterID AS Chapter, " +
"count(*) AS ProblemCount " +
"FROM Problem " +
"GROUP BY ChapterID";
dc = new OleDbCommand(strSQL, dbConn);
dr = dc.ExecuteReader( );
// set the source of the data for the repeater control and bind it
repChartBar.DataSource = dr;
repChartBar.DataBind( );
} finally { // clean up if (dbConn != null) { dbConn.Close( ); } } } } // Page_Load //************************************************************************ // // ROUTINE: repChartBar_ItemDataBound // // DESCRIPTION: This routine is the event handler that is called for // each item in the datagrid after a data bind occurs. It // is responsible for setting the width of the bar in the // passed repeater row to reflect the number of problems // in the chapter the row represents. //------------------------------------------------------------------------private void repChartBar_ItemDataBound(Object sender,
System.Web.UI.WebControls.RepeaterItemEventArgs e)
{
System.Web.UI.HtmlControls.HtmlImage img = null;
// get a reference to the image used for the bar in the row
img = (System.Web.UI.HtmlControls.HtmlImage)
(e.Item.FindControl("imgChartBar"));
// set the width to the number of problems in the chapter for this row
// multiplied by a constant to stretch the bar a bit more
img.Width =
(int)(((DbDataRecord)(e.Item.DataItem))["ProblemCount"]) * 10;
} // repChartBar_ItemDataBound
} // CH12CreateChartCS }