To read the article online, visit http://www.4GuysFromRolla.com/webtech/chapters/ASPNET2/ch02.4.shtml

ASP.NET: Tips, Tutorials, and Code
Chapter 2: Common ASP.NET Code Techniques


Saving Dynamically Created Images on the Web Server

When creating images on-the-fly, the two most useful classes are the Bitmap and Graphics classes. The Bitmap class represents an instance of an image. The Graphics class contains the various methods for drawing lines, curves, ellipses, rectangles, and other geometric shapes.

Rather than presenting a lengthy tutorial on these two classes, let's look at a code example and then examine the methods and properties used to create a dynamic image. We will, of course, only be looking at a small subset of all the graphic functions. To learn more about the numerous graphic functions, refer to the System.Drawing namespace documentation in the .NET Framework Reference.

Listing 2.4.1 contains the code for a function that creates a very rudimentary bar chart and saves it as a file on the server. The function, DrawBarGraph, has the following definition:

DrawBarGraph(title, X_Data, Y_Data)

The first parameter, title, is of type String and is used to give a title to the chart. X_Data and Y_Data are both ArrayLists: X_Data contains the X-axis labels for each data point, whereas Y_Data contains each data point (and needs to be an integral value). Lines 57 through 72 in Listing 2.4.1 illustrate how to set up the data points and call the DrawBarGraph function. Although Listing 2.4.1 uses hard-coded values for the X and Y axes, these values could have been easily pulled from a database. The output is shown in Figure 2.15.

Listing 2.4.1 With the .NET Framework You Can Create a Bar Chart On-the-Fly

 1: <%@ Import Namespace="System.Drawing" %>
 2: <%@ Import Namespace="System.Drawing.Imaging" %>
 3: <script language="VB" runat="server">
 4:  Sub DrawBarGraph(strTitle as String, aX as ArrayList, aY as ArrayList)
 5:   Const iColWidth as Integer = 60, iColSpace as Integer = 25, _
 6:      iMaxHeight as Integer = 400, iHeightSpace as Integer = 25, _
 7:      iXLegendSpace as Integer = 30, iTitleSpace as Integer = 50
 8:   Dim iMaxWidth as Integer = (iColWidth + iColSpace) * 
 aX.Count + iColSpace, _
 9:     iMaxColHeight as Integer = 0, _
10:     iTotalHeight as Integer = iMaxHeight + iXLegendSpace + iTitleSpace
11:
12:   Dim objBitmap as Bitmap = new Bitmap(iMaxWidth, iTotalHeight)
13:   Dim objGraphics as Graphics = Graphics.FromImage(objBitmap)
14:
15:   objGraphics.FillRectangle(new SolidBrush(Color.White), _
16:                0, 0, iMaxWidth, iTotalHeight)
17:   objGraphics.FillRectangle(new SolidBrush(Color.Ivory), _
18:                0, 0, iMaxWidth, iMaxHeight)
19:
20:   ' find the maximum value
21:   Dim iValue as Integer
22:   For Each iValue in aY
23:    If iValue > iMaxColHeight then iMaxColHeight = iValue
24:   Next
25:
26:   Dim iBarX as Integer = iColSpace, iCurrentHeight as Integer
27:   Dim objBrush as SolidBrush = new SolidBrush(Color.FromArgb(70,20,20))
28:   Dim fontLegend as Font = new Font("Arial", 11), _
29:     fontValues as Font = new Font("Arial", 8), _
30:     fontTitle as Font = new Font("Arial", 24)
31:
32:   ' loop through and draw each bar
33:   Dim iLoop as Integer
34:   For iLoop = 0 to aX.Count - 1
35:    iCurrentHeight = ((Convert.ToDouble(aY(iLoop)) / 
 Convert.ToDouble(iMaxColHeight)) * _
36:            Convert.ToDouble(iMaxHeight - iHeightSpace))
37:
38:    objGraphics.FillRectangle(objBrush, iBarX, _
39:        iMaxHeight - iCurrentHeight, iColWidth, iCurrentHeight)
40:    objGraphics.DrawString(aX(iLoop), fontLegend, objBrush, 
 iBarX, iMaxHeight)
41:    objGraphics.DrawString(String.Format("{0:#,###}", aY(iLoop)), 
 fontValues, objBrush, iBarX, _
42:               iMaxHeight - iCurrentHeight - 15)
43:
44:    iBarX += (iColSpace + iColWidth)
45:   Next
46:
47:   objGraphics.DrawString(strTitle, fontTitle, objBrush, _
48:       (iMaxWidth / 2) - strTitle.Length * 6, 
 iMaxHeight + iXLegendSpace)
49:   objBitmap.Save("C:\inetpub\wwwroot\graph.gif", ImageFormat.Gif)
50:
51:   objGraphics.Dispose()
52:   objBitmap.Dispose()
53:  End Sub
54:
55:
56:  Sub Page_Load(sender as Object, e as EventArgs)
57:   Dim aMonths as ArrayList = new ArrayList(), _
58:     aProfits as ArrayList = new ArrayList()
59:
60:   aMonths.Add("January")
61:   aMonths.Add("February")
62:   aMonths.Add("March")
63:   aMonths.Add("April")
64:   aMonths.Add("May")
65:
66:   aProfits.Add(240500)
67:   aProfits.Add(220950)
68:   aProfits.Add(283500)
69:   aProfits.Add(340000)
70:   aProfits.Add(325750)
71:
72:   DrawBarGraph("Profits!", aMonths, aProfits)
73:  End Sub
74: </script>

Listing 2.4.1 begins with two Import directives that are useful when creating images on-the-fly. Line 1 imports the System.Drawing namespace, which contains most of the many classes we use in the code: Bitmap, Graphics, Font, SolidBrush, and so on. Line 2 imports the System.Drawing.Imaging namespace, which contains the ImageFormat class that we use on line 49 to save the bar chart as a GIF image file.

The DrawBarGraph function begins on line 4, defined to accept three parameters: a string title, an ArrayList of labels for the X axis, and an ArrayList of data points. Next, on lines 5 through 7, a number of useful constants are defined, and on lines 8 through 10, the variables we'll need for this function are created and assigned values.

Figure 2.15
A bar chart with company profits has been dynamically created.

On line 12, an instance of the Bitmap class is created. The version of the Bitmap constructor we've used in this example expects two integer values, a width and a height, which are defined by iMaxWidth and iTotalHeight in our example. Next, on line 13, an instance of the Graphics class is created. The Graphics class does not contain a constructor; rather, to get an instance of the Graphics class, we must use the static method FromImage. FromImage expects an Image object as its lone parameter and returns a Graphics object. (Because the Bitmap class is derived from the Image class, we can simply pass our Bitmap instance, objBitmap, to the FromImage method.)

On line 15, the entire image is painted white using the FillRectangle method of the Graphics class. Keep in mind that the Graphics class contains the various functions to draw lines, rectangles, ellipses, and other geometric shapes. All these functions require either a Pen or Brush instance, depending on whether a line or filled image is being drawn. On line 17 another rectangle is drawn with the FillRectangle method, this one serving as the backdrop for the graphed data. For more information on the FillRectangle method (or for information on the Brush and SolidBrush classes), be sure to refer to the .NET Framework Documentation.

Because we only have a fixed height to draw all the bars in the bar graph, we must first determine the largest value in our set of data points so that we can scale all the data points relative to the largest one. On lines 21 through 24, a simple For Each ... Next loop is used to iterate through the ArrayList of data points to find the largest value. This largest value is stored in iMaxColHeight (line 23).

Lines 26 through 30 define the variables we'll need for the actual graphing of the bar chart. Line 27 creates a dark red solid brush; lines 28 through 30 create three instances of the Font class, each of the Arial font family with varying point sizes. The font instances fontLegend, fontValues, and fontTitle are used to write the X-axis labels (represented by the values in aX), the data point values, and the title of the chart, respectively.

The code in lines 34 through 45 does the actual work of creating the graph. Line 34 starts a For ... Next loop to iterate through each element of the aX ArrayList. It is essential that the X-axis label ArrayList (aX) and the data points ArrayList (aY) contain the same number of elements. Furthermore, realize that aX(N) represents the X-axis label for data point aY(N).

With each iteration of the loop, we will be drawing a particular bar in the bar chart. Lines 35 and 36 calculate the height for the current bar; this height is relative to the tallest bar in the bar chart. A ratio is derived with Convert.ToDouble(aY(iLoop)) / Convert.ToDouble(iMaxColHeight), which is then multiplied with the highest bar chart value to obtain the scaled height for the current bar.

On lines 38 and 39, the actual bar is drawn with the FillRectangle method. Line 40 draws the X-axis label at the bottom of the bar, whereas lines 41 and 42 draw the data point value at the top of the bar. This function assumes that the values of the data points are going to be numeric and, based on that assumption, formats the value using the String.Format method (line 41). Finally, line 44 increments the current x position, iBarX.

By the time the code execution reaches line 47, all the bars have been drawn and labeled. Line 47 simply adds the bar chart title to the image. Line 49 uses the Save method of the Image class (which the Bitmap class inherits). In Listing 2.4.1, the Save method expects two parameters: The first parameter it expects needs to be a string that represents the full path of the file to save to image to; the second parameter must be an instance of the ImageFormat class. The ImageFormat class defines the low-level details on how an image is to be physically stored. A number of static properties of the ImageFormat class return ImageFormat instances. A listing of these image formats can be seen in Table 2.3.

Table 2.3 The ImageFormat Class Contains a Number of Properties Representing Various Image Formats

Property

Description

Bmp

Specifies a bitmap image

Emf

Specifies the Windows enhanced metafile image format

Exif

Specifies the Exchangeable Image Format

Gif

Specifies the GIF image format

Icon

Specifies a Windows icon image format

Jpeg

Specifies the JPEG image format

MemoryBmp

Specifies the memory bitmap image format

Png

Specifies the PNG image format

Tiff

Specifies the TIFF image format

Wmf

Specifies the Windows metafile image format


After line 49 has executed, the bar chart has been saved as a file to the Web server, indicated by the path specified as the first parameter to the Save method. To conclude our DrawBarChart function, we clean up the resources used by calling the Dispose method for the Graphics class and Bitmap class instances.

In Listing 2.4.1, the DrawBarChart function is called from the Page_Load event handler. Before calling the function, however, two ArrayLists must be built and populated with the X-axis labels and data points. These two ArrayLists are created in Listing 2.4.1 on lines 57 and 58. The X-axis labels are populated in lines 60 through 64; the data points are assigned in lines 66 through 70. On line 72, the DrawBarChart function is called and passed the two ArrayLists along with the chart's title, "Profits!"

Sending Dynamically Created Images to the Browser

In the previous section, we examined how to create and save a dynamic image to the Web server's file system through an ASP.NET page. Although this capability is quite useful, at times it would be nice to bypass this step and stream the dynamically created image directly to the Web visitor's browser.

Fortunately, this is possible with the Save method of the Image class. In Listing 2.4.1, we looked at one use of the Save method, passing it a file path on the Web server and an ImageFormat. The Save method can also accept a Stream object as its first parameter; in doing so, the Save method will send the output of the image to the stream as opposed to disk.

With ASP.NET, the Response object has a number of new properties and methods. One such property is the OutputStream property, which provides programmatic access to the binary data being sent to the client's browser. The Save method of the Image class can use the Response object's OutputStream property to send a dynamically created, in-memory image directly to the client! Listing 2.4.2 contains a short code example in which a standard advertising banner is created on-the-fly and squirted to the user's browser.

Listing 2.4.2 Dynamically Created Images Can Be Sent Directly to the User's Browser

 1: <%@ Import Namespace="System.Drawing" %>
 2: <%@ Import Namespace="System.Drawing.Imaging" %>
 3: <script language="VB" runat="server">
 4: Sub Page_Load(sender as Object, e as EventArgs)
 5:  Dim objBitmap as Bitmap = new Bitmap(468, 60)
 6:  Dim objGraphics as Graphics = Graphics.FromImage(objBitmap)
 7:  Dim objBrush as SolidBrush = new SolidBrush(Color.FromArgb(0,80,80)), _
 8:    objBlackBrush as SolidBrush = new SolidBrush(Color.Black), _
 9:    objYellowBrush as SolidBrush = new SolidBrush(Color.Yellow)
10:
11:  Dim fontTitle as Font = new Font("Arial", 24), _
12:    fontSubtitle as Font = new Font("Arial Black", 9)
13:
14:  objGraphics.FillRectangle(new SolidBrush(Color.Ivory), 0, 0, 468, 60)
15:  objGraphics.DrawString("When you think ASP, think...", _
16:             fontSubtitle, objBlackBrush, 5, 8)
17:  objGraphics.DrawString("4GuysFromRolla.com", fontTitle, objBrush, 10, 20)
18:
19:  ' Draw a smiley face.. first draw the yellow circle!
20:  objGraphics.FillEllipse(objYellowBrush, 375, 5, 50, 50)
21:
22:  ' Now create the eyes.
23:  objGraphics.FillEllipse(objBlackBrush, 387, 20, 6, 6)
24:  objGraphics.FillEllipse(objBlackBrush, 407, 20, 6, 6)
25:
26:  ' And finally the smile.
27:  Dim aPoints(3) as Point
28:  aPoints(0).X = 383 : aPoints(0).Y = 35
29:  aPoints(1).X = 395 : aPoints(1).Y = 45
30:  aPoints(2).X = 405 : aPoints(2).Y = 45
31:  aPoints(3).X = 417 : aPoints(3).Y = 35
32:  objGraphics.DrawCurve(new Pen(Color.Black), aPoints)
33:
34:  Response.ContentType = "image/jpeg"
35:  objBitmap.Save(Response.OutputStream, ImageFormat.JPEG)
36:
37:  objGraphics.Dispose()
38:  objBitmap.Dispose()
39: End Sub
40: </script>

Listing 2.4.2 begins as Listing 2.4.1 did: by Importing the System.Drawing and System.Drawing.Imaging namespaces. Next, in the Page_Load event handler, a 468x60 Bitmap instance is created (the dimensions of the standard advertising banner; on line 5). On line 6, a Graphics instance is created and instantiated with the Graphics.FromImage method. On lines 7 through 9, a number of SolidBrushes are created and instantiated; lines 11 and 12 create the two Fonts we'll be using.

From lines 14 through 32, a number of Graphics methods are called to create the image. Finally, after the image is created, it's time to send it to the browser. Before we send the actual binary content to the browser, however, we set the binary data's ContentType as "image/jpeg" (line 34). This ContentType informs the browser how to display the binary information that it is receiving.

To actually send the binary data of the image, we use the Save method. Note that, on line 35, instead of specifying a filename as the first parameter, we specify the Stream to write the image to. Because we want to send the image to the browser, we specify that the OutputStream of the Response object be used. Also, the image should be converted and sent as a JPEG. Listing 2.4.2 concludes with a little housecleaning on lines 37 and 38.

One way to view the dynamically created image in Listing 2.4.2 is to visit the ASP.NET page that creates it and squirts it out to the OutputStream of the Response object. Figure 2.16 depicts a browser visiting the ASP.NET page directly.

Figure 2.16
The dynamically created image can be viewed by directly visiting the ASP.NET page.

Ideally, it would be nice to be able to display both content and the dynamically generated graphic on the same page. This, however, cannot be done through the same ASP.NET page; for example, if you were to add Response.Write("Hello, World!") in line 33 of Listing 2.4.2, instead of seeing an image when visiting the ASP.NET page, you'd see a broken image link. Essentially, this is because the browser is being sent "Hello, World!" and then the binary data for the image, and it assumes that the "Hello, World" is part of the binary data for the image.

If you want to display both textual content and a dynamically created image, you must create a standard HTML or ASP.NET page and then refer to the dynamic image content using HTML IMG tags. Listing 2.4.3 contains a very short example of an ASP.NET page that displays HTML content and the dynamic image created in Listing 2.4.2. Listing2.4.2.aspx, the ASP.NET page that creates the image, is referred to in the IMG tag as if it were a JPG image itself. Figure 2.17 contains a screenshot of Listing 2.4.3 when viewed through a browser.

Listing 2.4.3 To View a Dynamically Created Image from an HTML Page, Use the IMG tag

 1: <html>
 2: <body>
 3:  <h1>Hello, World!</h1>
 4:  Here is a picture of my banner!
 5:  <p>
 6:  <img src="Listing2.4.2.aspx" border="1"
 7:    alt="Dynamically created banner!">
 8: </body>
 9: </html>

Figure 2.17
The output of Listing 2.4.3, when viewed through a browser.


Tip

When viewing a dynamically created image from an ASP.NET page through an IMG tag, you can pass parameters through the QueryString such as: <img src="ASP.NET_Page.aspx?id=4">. The ASP.NET page can then read the QueryString value and alter its actions based on such information!


  • Read Part 5


  • Article Information
    Article Title: Common ASP.NET Code Techniques
    Article Author: Scott Mitchell
    Published Date: Friday, September 28, 2001
    Article URL: http://www.4GuysFromRolla.com/webtech/chapters/ASPNET2/ch02.4.shtml


    Copyright 2017 QuinStreet Inc. All Rights Reserved.
    Legal Notices, Licensing, Permissions, Privacy Policy.
    Advertise | Newsletters | E-mail Offers