
Pro ASP.NET 2.0 In CSharp 2005 (2005) [eng]
.pdf
1038 C H A P T E R 3 0 ■ DY N A M I C G R A P H I C S A N D G D I +
{
get {return caption;} set {caption = value;}
}
public PieSlice(string caption, float dataValue)
{
Caption = caption; DataValue = dataValue;
}
public override string ToString()
{
return Caption + " (" + DataValue.ToString() + ")";
}
}
The PieSlice class overrides the ToString() method to facilitate display in a data-bound ListBox. When a ListBox contains custom objects, it calls the ToString() method to get the text to show. (Another approach would be to use a GridView with a custom template.)
The test page (shown in Figure 30-12) has the responsibility of letting the user create pie slices. Essentially, the user enters a label and a numeric value for the slice and clicks the Add button. The PieSlice object is then created and shown in a ListBox.
Figure 30-12. A dynamic pie chart page
The amount of code required is fairly small. The trick is that every time the page is finished processing (and the Page.PreRender event fires), all the PieSlice objects in the ListBox are stored in session state. Every time the page is requested (and the Page.Load event fires), any available PieSlice objects are retrieved from session state.

C H A P T E R 3 0 ■ DY N A M I C G R A P H I C S A N D G D I + |
1039 |
Here’s the complete code for the test page:
public partial class CreateChart : System.Web.UI.Page
{
// The data that will be used to create the pie chart. private ArrayList pieSlices = new ArrayList();
protected void Page_Load(object sender, System.EventArgs e)
{
// Retrieve the pie slices that are defined so far. if (Session["ChartData"] != null)
{
pieSlices = (ArrayList)Session["ChartData"];
}
}
protected void cmdAdd_Click(object sender, System.EventArgs e)
{
// Create a new pie slice.
PieSlice pieSlice = new PieSlice(txtLabel.Text, Single.Parse(txtValue.Text));
pieSlices.Add(pieSlice);
// Bind the list box to the new data. lstPieSlices.DataSource = pieSlices; lstPieSlices.DataBind();
}
protected void CreateChart_PreRender(object sender, System.EventArgs e)
{
//Before rendering the page, store the current collection
//of pie slices.
Session["ChartData"] = pieSlices;
}
}
The pie drawing code is quite a bit more involved. It creates a new bitmap, retrieves the PieSlice objects, examines them, and draws the corresponding pie slices and legend.
The first step is to create the drawing surface and retrieve the chart data from session state, as follows:
protected void Page_Load(object sender, System.EventArgs e)
{
Bitmap image = new Bitmap(300, 200); Graphics g = Graphics.FromImage(image);
g.FillRectangle(Brushes.White, 0, 0, 300, 200); g.SmoothingMode = SmoothingMode.AntiAlias;
if (Session["ChartData"] != null)
{
// Retrieve the chart data.
ArrayList chartData = (ArrayList)Session["ChartData"];
...

1040 C H A P T E R 3 0 ■ DY N A M I C G R A P H I C S A N D G D I +
Next, the drawing code adds a title to the chart, as shown here:
...
// Write some text to the image. g.DrawString("Sample Chart",
new Font("Verdana", 18, FontStyle.Bold), Brushes.Black, new PointF(5, 5));
...
The next step is to calculate the total of all the data points, as follows. This allows you to size each slice proportionately in the pie.
...
// Calculate the total of all data values. float total = 0;
foreach (PieSlice item in chartData)
{
total += item.DataValue;
}
...
Once you know the total, you can calculate the percentage of the pie that each slice occupies. Finally, you can multiply this percentage by the total angle width of a circle (360 degrees) to find the angle width required for that slice.
To draw each slice, you can use the Graphics.FillPie() method and specify the starting and ending angle. When you draw each slice, you also need to ensure that you choose a new color that hasn’t been used for a previous slice. This task is handled by a GetColor() helper method, which chooses the color from a short list based on the slice’s index number:
...
// Draw the pie slices.
float currentAngle = 0, totalAngle = 0; int i = 0;
foreach (PieSlice item in chartData)
{
currentAngle = item.DataValue / total * 360; g.FillPie(new SolidBrush(GetColor(i)), 10, 40, 150, 150,
(float)Math.Round(totalAngle),
(float)Math.Round(currentAngle)); totalAngle += currentAngle;
i++;
}
...
The last drawing step is to render the legend. To create the legend, you need a rectangle that shows the slice color, followed by the pie slice label. Once again, the GetColor() method returns the correct color for the slice:
...
// Create a legend for the chart.
PointF colorBoxPoint = new PointF(200, 83); PointF textPoint = new PointF(222, 80);
i = 0;
foreach (PieSlice item in chartData)
{

C H A P T E R 3 0 ■ DY N A M I C G R A P H I C S A N D G D I + |
1041 |
g.FillRectangle(new SolidBrush(GetColor(i)), colorBoxPoint.X, colorBoxPoint.Y, 20, 10);
g.DrawString(item.Caption, new Font("Tahoma", 10), Brushes.Black, textPoint);
colorBoxPoint.Y += 15; textPoint.Y += 15; i++;
}
...
Finally, you can render the image. In this case, GIF format is acceptable because the drawing code uses a fixed set of colors that are all in the basic 256-color GIF palette, as follows:
image.Save(Response.OutputStream, ImageFormat.Gif);
}
}
The only detail that has been omitted so far is the GetColor() method, which returns a color for each pie slice, as shown here:
private Color GetColor(int index)
{
// Support six different colors. This could be enhanced. if (index > 5)
{
index = index % 5;
}
switch (index)
{
case 0:
return Color.Red; case 1:
return Color.Blue; case 2:
return Color.Yellow; case 3:
return Color.Green; case 4:
return Color.Orange; case 5:
return Color.Purple; default:
return Color.Black;
}
}
In its current implementation, GetColor() starts to return the same set of colors as soon as you reach the seventh slice, although you could easily change this behavior.
The end result is that both pages work together without a hitch. Every time a new slice is added, the image is redrawn seamlessly.
You could do a fair amount of work to improve this chart. For example, you could make it more generic so that it could render to different sizes, display larger amounts of data in the legend, and provide different labeling options. You could also render different types of charts, such as line charts and bar graphs.

1042 C H A P T E R 3 0 ■ DY N A M I C G R A P H I C S A N D G D I +
■Tip To see a more ambitious pie chart and bar chart renderer, you can download a free starter kit sample from Microsoft’s ASP.NET website at http://www.asp.net/ReportsStarterKit. This sample is available in C# and VB .NET, and you can customize it as you desire. Although it sports improved drawing logic, the mechanism used to transfer information is somewhat limited. Because it uses the query string, there’s a limit to how much chart data you can specify. Of course, nothing is stopping you from improving the example to support other options, such as session state.
Summary
In this chapter, you learned how to master basic and advanced GDI+. Although these techniques aren’t right for every web page, they give you a set of features that can’t be matched by many other web application programming frameworks. You also explored how to create server-side image maps with the ImageMap control. For even more detailed information about how GDI+ works and how to optimize it, you may want to check out Pro .NET Graphics Programming (Apress, 2005).


1044 CHAPTER 31 ■ PORTALS WITH WEB PART PAGES
Figure 31-1. MSN: a good example of a personalized home page
With the ASP.NET 2.0 Web Parts Framework, you can create customizable web pages on your own easily. The framework consists of controls and components that perform the following work for you:
•Defining customizable sections: The framework allows you to structure your page and specify customizable sections of the page through web part zones.
•Offering components for item selection: In addition to customizable sections, the framework ships with special sections that allow you to edit properties for information items displayed on the page or to add and remove information items to/from the page.
•Customizing the web page: As soon as the user is logged into your application, she can customize the web page by dragging and dropping items displayed on the web page onto different customizable sections. The user can even close or minimize content to create more space for other, more interesting content.
•Saving the customized appearance: ASP.NET automatically saves the user’s personalized appearance of the web page through its personalization infrastructure.



CHAPTER 31 ■ PORTALS WITH WEB PART PAGES |
1047 |
WebPartManager and WebPartZones
After you have created the web page’s design, you can continue adding the first web part controls to your page. These controls are summarized in the WebParts section of Visual Studio’s Toolbox. For this example, the first control to add at the bottom of your page is the WebPartManager control. The WebPartManager works with all the zones added to the web page and knows about all the web parts available for the page. It furthermore manages the personalization and makes sure the web page is customized for the currently logged-on user. The following code snippet shows the modified portion of the page code:
<form id="form1" runat="server"> <div>
<asp:WebPartManager runat="server" ID="MyPartManager" />
<table width="100%">
...
</table>
</div>
</form>
The WebPartManager also throws events you can catch in your application to perform actions when the user adds or deletes a web part or when a web part communicates to another web part. (You will learn more about web part communication later in the “Connecting Web Parts” section.)
After you have added the WebPartManager to the page, you can add customizable sections to your web part. These sections are called WebPartZones, and every zone can contain as much web parts as the user wants. With the WebPartZones added, the complete code looks as follows:
<form id="form1" runat="server"> <div>
<asp:WebPartManager runat="server" ID="MyPartManager" /> <table width="100%">
<tr valign="middle" bgcolor="#00ccff"> <td colspan="2">
<span style="font-size: 16pt; font-family: Verdana"> <strong>Welcome to web part pages!</strong>
</span>
</td>
<td>Menu</td>
</tr>
<tr valign="top"> <td width="20%">
<asp:CatalogZone runat="server" ID="SimpleCatalog"> </asp:CatalogZone>
</td>
<td style="width: 60%">
<asp:WebPartZone runat="server" ID="MainZone"> </asp:WebPartZone>
</td>
<td width="20%">
<asp:WebPartZone runat="server" ID="HelpZone"> </asp:WebPartZone>
</td>
</tr>
</table>
</div>
</form>