Microsoft & .NETASPRendering Graphics in ASP.NET with GDI+

Rendering Graphics in ASP.NET with GDI+

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

As I write this, Vista is out and promises new and cool things, and Windows Presentation Foundation (WPF) is closing the gap between rich Windows clients and rich web clients. So, a huge number of developers and businesses out there are interested in rich web clients. For them, I wrote this article.


This article uses a trivial example to demonstrate how to dynamically create GDI+ graphics in web pages. The example, a ticking clock, has plumbing that would support any kind of continuously updating, dynamically rendered graphic.


Rendering Graphics with GDI+


Although web forms do not have a canvas (or device context, also called DC) and you therefore cannot ask a Web form for its Graphics object, you can simulate this behavior by rendering graphics in ASP.NET. In summary, to render images with GDI+ you need:



  • One user control that will act as your device context or drawing canvas
  • One web page to contain the user control
  • One web page to contain an image control (The first web page will actually play the role of the image for this web page.)

Figure 1 depicts the relationship between the user control and two web pages.

Figure 1: The Relationship Between the Controls that Play the Role of Dynamic Canvas


One page is actually the viewable page, and the second page and user control play the role of dynamic canvas. Take a look at how this works by building it from the inside out.


Defining the UserControl


The UserControl is as close to the Graphics object (canvas or device context, if you prefer) as you are going to get. The basic idea of rendering the image is to create an image, get a Graphics object from that image, draw something on it, and then save that image on the HttpResponse.OutputStream (this code is show in Listing 1).


Listing 1: The Code That Orchestrates Rendering the Clock

Private Sub DrawClock()
Dim b As Bitmap = New Bitmap(FClockWidth, FClockHeight)
Dim g As Graphics = Graphics.FromImage(b)
Dim p As Pen = New Pen(Brushes.Black, 1)
g.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
DrawClockFace(g, p)
DrawClockTicks(g, p)
DrawClockHands(g, p)
Response.ContentType = “image/jpeg”
b.Save(Response.OutputStream, ImageFormat.Jpeg)
Response.End()

End Sub

The reason I used an inner UserControl is that writing to the output stream is destructive to things already on the stream. So, other HTML would be obliterated by writing the image. When you save the image to the output response stream, that is all that will be there. The code that renders the clock is not that important technically, but Listing 2 shows it for fun.


Listing 2: The ClockControl (The ClockControl.ascx Page Is an Empty UserControl.)

Imports System.Drawing
Imports System.Drawing.Imaging
Imports System.Runtime.InteropServices
Partial Class ClockControl
Inherits System.Web.UI.UserControl
Private FClockWidth As Integer = 100
Private FClockHeight As Integer = 100
Private Sub DrawClock()
Dim b As Bitmap = New Bitmap(FClockWidth, FClockHeight)
Dim g As Graphics = Graphics.FromImage(b)
Dim p As Pen = New Pen(Brushes.Black, 1)
g.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
DrawClockFace(g, p)
DrawClockTicks(g, p)
DrawClockHands(g, p)
Response.ContentType = “image/jpeg”
b.Save(Response.OutputStream, ImageFormat.Jpeg)
Response.End()
End Sub
Private Sub DrawClockFace(ByVal g As Graphics, ByVal p As Pen)
g.FillRectangle(Brushes.White, 0, 0, FClockWidth, FClockHeight)
g.DrawEllipse(p, New Rectangle(1, 1, 98, 98))
End Sub
Private Sub DrawClockTicks(ByVal g As Graphics, ByVal p As Pen)
‘ draw the clock ticks
‘ Borrowed from Mike Gold’s article:
‘ http://www.vbdotnetheaven.com/UploadFile
‘/mgold/VirtualClockinVBdotNET04212005032826AM/
‘VirtualClockinVBdotNET.aspx
Dim count As Integer = 1
Dim hour As Integer = 1
Dim angle As Double
For count = 0 To 330 Step 30
angle = (count – 1) * Math.PI / 180
‘For angle = 0 To (2 * Math.PI) – (2.0 * Math.PI / 14)
‘Step 2.0 * Math.PI / 12
Dim x As Double = (FClockWidth – 20) / 2 *_
Math.Cos((angle – Math.PI / 3)) + _
(FClockWidth – 20) / 2 + 5
Dim y As Double = (FClockWidth – 20) / 2 * _
Math.Sin((angle – Math.PI / 3)) + _
(FClockWidth – 20) / 2 + 4
Dim font As Font = New Font(“Times New Roman”, 8)
g.DrawString(Convert.ToString(hour), font, Brushes.Black, _
CSng(x), CSng(y), _
New StringFormat)
‘count += 1
hour += 1
Next count ‘ angle
End Sub
Private Sub DrawClockHands(ByVal g As Graphics, ByVal p As Pen)
Dim d As DateTime = DateTime.Now
‘ draw hour hand
Dim h As Integer = d.Hour
DrawHour(g, h)
‘ draw minute hand
Dim m As Integer = d.Minute
DrawMinute(g, m)
‘ draw second hand
Dim s As Integer = d.Second
DrawSecond(g, s)
End Sub
Private Sub DrawHour(ByVal g As Graphics, ByVal hour As Integer)
Const OFFSET As Integer = 30
Dim p As Pen = New Pen(Color.Black, 3)
‘ Figure out the Angle in radians
Dim angle As Double = ((hour – 1) Mod 12) * 30 * Math.PI / 180
Dim x, y As Double
x = (FClockWidth – OFFSET) / 2 * _
Math.Cos((angle – Math.PI / 3)) + _
(FClockWidth – OFFSET) / 2 + 10
y = (FClockWidth – OFFSET) / 2 * _
Math.Sin((angle – Math.PI / 3)) + _
(FClockWidth – OFFSET) / 2 + 8

g.DrawLine(p, CSng(FClockWidth / 2), CSng(FClockHeight / 2), _
CSng(x), CSng(y))
End Sub
Private Sub DrawMinute(ByVal g As Graphics, _
ByVal Minute As Integer)
Dim p As Pen = New Pen(Color.Black, 2)
‘ Figure out the Angle in radians
Dim angle As Double = (Minute – 6) * 6 * Math.PI / 180
Dim x, y As Double
x = (FClockWidth – 20) / 2 * Math.Cos((angle – Math.PI / 3)) + _
(FClockWidth – 20) / 2 + 10
y = (FClockWidth – 20) / 2 * Math.Sin((angle – Math.PI / 3)) + _
(FClockWidth – 20) / 2 + 8
g.DrawLine(p, CSng(FClockWidth / 2), CSng(FClockHeight / 2), _
CSng(x), CSng(y))
End Sub
Private Sub DrawSecond(ByVal g As Graphics, _
ByVal Second As Integer)
Dim p As Pen = New Pen(Color.Red, 1)
‘ Figure out the Angle in radians
Dim angle As Double = (Second – 6) * 6 * Math.PI / 180
Dim x, y As Double
x = (FClockWidth – 20) / 2 * Math.Cos((angle – Math.PI / 3)) + _
(FClockWidth – 20) / 2 + 10
y = (FClockWidth – 20) / 2 * Math.Sin((angle – Math.PI / 3)) + _
(FClockWidth – 20) / 2 + 8
g.DrawLine(p, CSng(FClockWidth / 2), CSng(FClockHeight / 2), _
CSng(x), CSng(y))
End Sub

Protected Sub Page_Load(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Me.Load
DrawClock()
End Sub
End Class


The last three lines of code in DrawClock move the dynamic image to the output stream. You will need to use System.Drawing and System.Drawing.Image to support the ClockControl behavior.

Showing the Dynamic Image


To render the image, you can create a page—the example calls ClockPage.aspx—and drag and drop the ClockControl onto the ClockPage (see Figure 2).



Figure 2: The Dynamically Drawn Image of a Simple Analog Clock


When the ClockPage.aspx page is requested, the ClockControls Page_Load calls DrawClock. To ensure the ClockPage is requested, you assign it instead of an actual image to the source property of an <img> control (keeping in mind that an Image web control is rendered as an <img> HTML tag). Listing 3 shows the markup for the ClockPage.


Listing 3: The Markup for the clockpage.sspx Page

<%@ Page Language=”VB” AutoEventWireup=”false”
CodeFile=”ClockPage.aspx.vb” Inherits=”ClockPage” %>
<%@ Register Src=”ClockControl.ascx”
TagName=”ClockControl” TagPrefix=”uc1″ %>
<%@ OutputCache NoStore=”true” Duration=”1″ VaryByParam=”none” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html >
<head runat=”server”>
<title>Untitled Page</>title>
</head>
<body>
<form id=”form1″ runat=”server”>
<div>
<uc1:ClockControl ID=”ClockControl1″ runat=”server”
EnableViewState=”false” />
</div>
</form>
</body>
</html>

I’ll leave the final challenge of animating the clock as real-time feedback for another article. As is, the user would have to refresh the page every time an update clock was desired.


The Clock Is Drawn


This example demonstrated how you can treat a dynamic image on a UserControl as a drawing surface much as you’d treat a Windows form as one. The user control is then embedded on a page, and that page becomes the source value of an image attribute. Each time the source attribute—I used the <img src=”clockpage.aspx” /> tag—requested and the image is to be rendered, the clock will be redrawn.


About the Author


Paul Kimmel is the VB Today columnist for www.codeguru.com and has written several books on object-oriented programming and .NET. Check out his new book UML DeMystified from McGraw-Hill/Osborne. Paul is a software architect for Tri-State Hospital Supply Corporation. You may contact him for technology questions at pkimmel@softconcepts.com.


If you are interested in joining or sponsoring a .NET Users Group, check out www.glugnet.org.


Copyright © 2007 by Paul T. Kimmel. All Rights Reserved.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories