Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Pro ASP.NET 2.0 In CSharp 2005 (2005) [eng]

.pdf
Скачиваний:
104
Добавлен:
16.08.2013
Размер:
29.8 Mб
Скачать

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).

C H A P T E R 3 1

■ ■ ■

Portals with Web Part Pages

Websites are more sophisticated than ever. Nowadays it’s not enough if a website has a great look and feel. It has to be easy to use and must present exactly the information that users want to see. In addition, users want websites to present this information in a specific way—based on their individual preferences. Therefore, personalization and user profiles have become more important in web development.

But users want to be able to customize more than simple profile information. They want to be able to customize the website’s user interface to fit their requirements with the goal of accessing the information they need for their daily business as soon as they are logged in. So, in this chapter, you will learn how you can create modular and dynamically configurable web pages to fulfill these sorts of requirements using the ASP.NET 2.0 Web Parts Framework and personalization features.

Typical Portal Pages

In a personalized environment, users want specific information stored in a profile, as you learned in Chapter 24. Furthermore, users want to be able to customize most of a website’s appearance and the information it displays.

A good example of a personalized website is Microsoft’s MSN. As soon as you log into MSN, you can configure the information displayed on your personal home page. For example, MSN allows you to select the types of information items you can see and then displays those pieces of information on your personal home page, as shown in Figure 31-1.

Some of the information items you can select are simple, such as the search item displayed in the upper-right corner of Figure 31-1, and others are more complex such as the stock quotes listed in the bottom-right corner. Interestingly, you have many more possibilities than just selecting information items. You can specify where the information is displayed on the page by dragging items into different positions on the web page. When you log off and then later return to the page and log in, all the changes you have made will be present—the page design will appear exactly how you

left it.

These types of pages define content areas where the user can add or remove information items. The user can select the information items from a list of available items, which are nothing more than reusable user interface elements (or controls in ASP.NET), and add them to the specified content areas of the web page. In most cases, a portal page defines multiple content areas: a main area in the center of the page for displaying the most important information, a navigational area in the left or right section of the page, and optionally another area (either on the left or right side of the page) for small items (such as a weather item or a quick-links list). Of course, most web pages also include a header and footer (which you can create easily with master pages).

1043

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

1045

A page that uses this framework is called a web part page, and the information items that can be displayed on the page are called web parts. All the pieces you put together to display on the page are controls, as you will see in the next section. Therefore, to create web part pages, you just need to know how to put all your custom and prebuilt controls together to create a customizable page. You will learn the details of how to do this in this chapter.

Basic Web Part Pages

The first thing you need to know is how to create a basic web part page. In the following sections, you will learn the major steps for creating such a page. After that, you will learn how to create web parts, the information items that go on the web part page.

The steps for creating a web part page are as follows:

1.Create the page: Create a simple ASP.NET page as usual with Visual Studio .NET. You don’t need any special type of page—this is an .aspx page just as any other page. Before you continue, you can structure the layout of your page using HTML tables to create, for example, a page with a navigation area, a main area, and a side panel for additional information (similar to the MSN page presented in Figure 31-1).

2.Add a WebPartManager: Next, you need to add a WebPartManager control to your page. This is an invisible control that knows about all the available web parts on a page and manages personalization. The WebPartManager needs to be the first control created on a web part page, because every other web part–related control depends on it.

3.Add WebPartZones: Every section on the page that should display your custom web parts is encapsulated in an instance of the WebPartZone control. Add a WebPartZone control on every section of your page that should contain web parts and should be customizable.

4.Add web parts: You can use simple user controls, prebuilt user controls, custom server controls, or controls directly inherited from WebPart. You can place all these controls into a web part zone using the Visual Studio designer or using the code. The ASP.NET infrastructure does the rest automatically.

5.Add prebuilt zones and parts: If the user wants to add or remove web parts at runtime or edit properties of web parts, you need to add prebuilt zones to your web page, such as the CatalogZone (which allows the user to add web parts to the page).

After you have completed these steps, your web part page is ready to use. Remember that you need to include authentication (either Windows or forms authentication) to your application so that the framework can store personalized information on a per-user basis. By default this information is stored in the SQL Server 2005 file-based database ASPNETDB.MDF, which is automatically created in the App_Data directory if you have SQL Server 2005 installed. Otherwise, you need to create the database on SQL Server using aspnet_regsql.exe, as described in Chapter 21 (personalization information is stored in the same database as user information). Of course, as is the case with any other part of the framework, and as you have learned for the Membership and Roles APIs, your custom provider can replace the personalization infrastructure without affecting the application itself.

Creating the Page Design

The first step of creating a web part page is to create an .aspx page in your solution. You don’t have to add a special item—just add a simple web form to your project. Afterward, you can structure the basic layout of your page as you’d like.

1046 CHAPTER 31 PORTALS WITH WEB PART PAGES

The following example uses a simple HTML table to structure the page with a main center area, a configuration area on the left, and a simple information area on the right:

<form id="form1" runat="server"> <div>

<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%"> </td>

<td style="width: 60%"> </td>

<td width="20%"> </td>

</tr>

</table>

</div>

</form>

The first table row is just a simple header for the application. Within the second row, the table contains three columns: the left one will be used as a column for configuration controls (such as a control for selecting available web parts), the center column will be used for displaying the main information, and the right column will be used for little web parts with additional information. Notice that the first row includes a second column for a menu; you will use this menu later for switching between the modes of the page (for example, from the Browse mode that merely displays information to the Design mode that allows the user to move web parts from one zone to another). You can see the page layout in Figure 31-2.

Figure 31-2. The basic layout of the page

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>