ASP.NET: Drawing images and bar charts with System.Drawing
From Wiki
Summary: An example of how we can create an image and even bar charts by using the System.Drawing namespace.
Drawing your own images in ASP.NET can be achieved by using what is known as GDI+. To work with GDI+, we can use the System.Drawing namespace which provides direct access to GDI+ basic graphics functionality.
For this example, we'll be working specifically with the System.Drawing.Graphics namespace which will allow us to draw rectangles and lines.
To demonstrate these methods, we'll create a simple example that simply draws a rectangle out to our page. Firstly, we'll need to import these two namespaces:
- Imports System.Drawing
- Imports System.Drawing.Imaging
Then, let's create a simple 100x100 image with a small rectangle inside it:
- Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
- ' Tell the page to expect an image
- Response.ContentType = "image/JPEG"
- ' Create a new 100x100 bitmap and graphics object
- Dim b As New Bitmap(100, 100)
- Dim g As Graphics = Graphics.FromImage(b)
- ' Add a 20x20 white square
- g.DrawRectangle(Pens.White, 20, 20, 20, 20)
- ' Stream the image out to the client
- b.Save(Response.OutputStream, ImageFormat.Jpeg)
- End Sub
When you run this code on your development machine, you should hopefully see a small image with the rectangle drawn inside it. To do this, you'll notice that we used the Graphics.DrawRectangle method to draw a rectangle specified by a coordinate pair, a width, and a height.
Now that we've gone through a simple example of creating a rectangle, we can then take this knowledge and build a series of rectangles to form a bar chart. First, we need to create a page that will contain an image. This image will point at our bar chart page which will return the bar chart image. We'll also pass the x and y axis values to this page as part of the querystring so that the bar chart page knows what to create. I'm just going to be hard-coding these values for this example but, if you wanted, you could make the image runat="server" or use an asp:Image control so that you can set the values from your code-behind page. Anyway, here's the example page:
- <%@ Page Language="VB" AutoEventWireup="false" CodeFile="Default1.aspx.vb" Inherits="Default1" %>
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
- <html xmlns="http://www.w3.org/1999/xhtml" >
- <head runat="server">
- <title>Untitled Page</title>
- </head>
- <body>
- <form id="form1" runat="server">
- <div>
- <img src="barchart.aspx?xaxis=A,B,C,D,E,F,G,H,I,J,K&yaxis=2,4,6,8,10,12,14,16,18,20,22" alt="bar chart"/>
- </div>
- </form>
- </body>
As you'll see from above, we've simply set up a comma seperated list of x and y values which will form the basis of our bar chart page. We don't need any code in the aspx portion of our bar chart page as it will be the code behind page that is responsible for outputting the image. So, create a new page called barchart.aspx and add the following code to the code behind page:
- Imports System.Drawing
- Imports System.Drawing.Imaging
- Partial Class BarChart
- Inherits(System.Web.UI.Page)
- Private rndColor As New Random
- Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
- ' Declarations
- Dim intSpacing As Integer = 40
- Dim intBarWidth As Integer = 20
- Dim intBarScale As Integer = 10
- Dim intMaxHeight As Integer = 0
- Dim intMaxWidth As Integer = 0
- Dim gWidth As Graphics = Graphics.FromImage(New Bitmap(1, 1))
- Dim strYAxis As String()
- Dim strXAxis As String()
- ' 1) Get an array of each axis value and description
- strYAxis = Request.QueryString("yaxis").Split(",")
- strXAxis = Request.QueryString("xaxis").Split(",")
- ' 2) Get the maximum height for the chart
- For intHeights As Integer = 0 To strYAxis.Length - 1
- If CInt(strYAxis(intHeights)) > intMaxHeight Then intMaxHeight = CInt(strYAxis(intHeights))
- Next
- ' 3) Add 40 pixels to it to compensate for the labels
- intMaxHeight = intMaxHeight * intBarScale + 40
- ' 4) Get the maximum width of any x or y axis string to work out the bar chart width
- For intWidths As Integer = 0 To strXAxis.Length - 1
- If CInt(gWidth.MeasureString(strXAxis(intWidths), New Font("Courier New", 10, FontStyle.Italic)).Width) > intMaxWidth Then
- intMaxWidth = CInt(gWidth.MeasureString(strXAxis(intWidths), New Font("Courier New", 10, FontStyle.Italic)).Width)
- End If
- Next
- For intWidths As Integer = 0 To strYAxis.Length - 1
- If CInt(gWidth.MeasureString(strYAxis(intWidths), New Font("Courier New", 10, FontStyle.Italic)).Width) > intMaxWidth Then
- intMaxWidth = CInt(gWidth.MeasureString(strYAxis(intWidths), New Font("Courier New", 10, FontStyle.Italic)).Width)
- End If
- Next
- intBarWidth = intMaxWidth
- ' 5) Create a new bitmap and graphics object based on the size of the number of arrary elements
- Dim b As New Bitmap(CInt((strXAxis.Length * intBarWidth) + (strXAxis.Length * intSpacing) + (intSpacing / 2)), intMaxHeight)
- Dim g As Graphics = Graphics.FromImage(b)
- ' 6) Set the background colour
- g.Clear(Color.White)
- ' 7) Draw each bar
- For i As Integer = 0 To strYAxis.Length - 1
- ' Add a border for the coloured bar
- g.DrawRectangle(Pens.Black, (i * intSpacing) + (i * intBarWidth) + 15, _
- intMaxHeight - (CInt(strYAxis(i)) * intBarScale), intBarWidth, (CInt(strYAxis(i)) * intBarScale))
- ' Add the coloured bar
- g.FillRectangle(New SolidBrush(RandomRGBColor), (i * intSpacing) + (i * intBarWidth) + 15, _
- intMaxHeight - (CInt(strYAxis(i)) * intBarScale), intBarWidth, (CInt(strYAxis(i)) * intBarScale))
- ' Add the x axis values to the bar chart
- g.DrawString(strXAxis(i), New Font("Courier New", 10, FontStyle.Italic), Brushes.Black, _
- New PointF((i * intSpacing) + (i * intBarWidth) + 15, intMaxHeight - 25 - (CInt(strYAxis(i)) * intBarScale)))
- ' Add the y axis values to the bar chart
- g.DrawString(strYAxis(i), New Font("Courier New", 10, FontStyle.Italic), Brushes.White, _
- New PointF((i * intSpacing) + (i * intBarWidth) + 15, intMaxHeight - (CInt(strYAxis(i)) * intBarScale)))
- Next
- ' 8) Add a border to the chart
- g.DrawRectangle(Pens.Black, 1, 1, _
- CInt((strXAxis.Length * intBarWidth) + (strXAxis.Length * intSpacing) + (intSpacing / 2)) - 2, intMaxHeight - 2)
- ' 9) Stream the image out to the client
- b.Save(Response.OutputStream, ImageFormat.Jpeg)
- End Sub
- Public Function RandomRGBColor() As Color
- ' Return a random color.
- Return (Color.FromArgb(255, rndColor.Next(0, 255), rndColor.Next(0, 255), rndColor.Next(0, 255)))
- End Function
- End Class
You can run that page on your own machine and see the resulting bar chart, but before you do that let's go through the nine steps that were taken to create this image:
1) Get an array of each axis value and description
As we passed the x and y values through to the page as querystring values, we need to split each one to get an array which we can use to find each corresponding value. We could also put some validation into this section to make sure that we have an equal number of values for each axis.
2) Get the maximum height for the chart
In order to work our the size of the bitmap that we need to draw, we loop through each value of the y axis and get the largest value. We can then assign this value to the height attribute of the bitmap.
3) Add 40 pixels to it to compensate for the labels
Before we assign the height, we add 40 pixels to the maximum height so that it allows us a bit of extra room to add the x axis values above each bar.
4) Get the maximum width of any x or y axis string to work out the bar chart width
To work out the width of each bar, I've employed a technique that uses the Graphics.MeasureString method to work out the maximum width of a string from either the x or y axis.
5) Create a new bitmap and graphics object based on the size of the number of array elements
We can now calculate the height and width of our bar chart based on factors such as the number of elements in our array, each bar size, the maximum height and the spacing between each bar.
6) Set the background colour
As you'll have noticed in our very first example, the default colour of the background was black, so using the Graphics.Clear method we can clear the entire drawing surface and fill it with the specified background color.
7) Draw each bar
There are 4 stages to this section.
The first will draw a rectangle based on the size of the y axis value also taking into account an Integer value called "intBarScale". This scale will be used to scale the value up by the relevant number of pixels (i.e. a value of 1 will become 10 if the scale is set to 10). The border of this rectangle will be set to black so that it appears around the coloured bar.
The second stage will create a rectangle of the same size, but no border will added and instead of using the Graphics.DrawRectangle method, we'll use Graphics.FillRectangle so that we can fill it with a random colour.
The third stage will add the x axis value just above the relevant bar and the fourth stage will add the y axis value just inside the bar itself.
8) Add a border to the chart
Here we use the same technique as above to draw a black border around the whole image.
9) Stream the image out to the client
We can now stream this image back to the calling page and set the image type.
As you can see from this example, it's relatively easy to draw images once you know how to interact with the Graphics namespace. There are some enhancements that you will need to make if you wanted to use this page for creating bar charts in a production environment (which you'll see if you start modifying the values that are passed through to the bar chart page) but I think it should be a very good starting point and you can now hopefully use this knowledge for any other image needs you have.
This Hack is part of the ASP.NET Hacks collection


