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

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

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

1008 C H A P T E R 2 9 J AVA S C R I P T

protected void Button1_Click(object sender, System.EventArgs e)

{

string url = "http://www.google.com";

string frameScript = "<script language='javascript'>" + "window.parent.content.location='" + url + "';</script>";

Page.ClientScript.RegisterStartupScript("FrameScript", frameScript);

}

Figure 29-17. Using server-side code to control frame navigation

Tip Oddly enough, in this example the RegisterClientScriptBlock() method probably works slightly better than the RegisterStartupScript() block method. No matter how you implement this approach, you will get a slight delay before the new frame is refreshed. Because the script block doesn’t depend on any of the controls on the page, you can render it immediately after the opening <form> tag using RegisterClientScriptBlock(), rather than at the end. This ensures that the JavaScript code that triggers the navigation is executed immediately, rather than after all the other content in the page has been downloaded.

Inline Frames

One solution that combines server-side programming with framelike functionality is the <iframe> tag (which is defined as part of the HTML 4.0 standard). The <iframe> in an inline, or embedded, frame that you can position anywhere inside an HTML document. Both the main document and the embedded page are treated as complete, separate documents.

C H A P T E R 2 9 J AVA S C R I P T

1009

Here’s an example of an <iframe> tag:

<iframe src="page.aspx" width="40%" height="80" align="right"> </iframe>

The key problem with the <iframe> tag is that support is not universal across all browsers. Internet Explorer has supported it since version 3, but Netscape added it only in version 6. However, you can define static text that will be displayed in browsers that don’t recognize the tag, as shown here:

<iframe src="page.aspx" width="40%" height="80" align="right"> <p>See the content at <a href="page.aspx">page.aspx</a>.</p>

</iframe>

Once you’ve added an <iframe> to your page, you can define it in the code-behind to access it programmatically. ASP.NET doesn’t have a control class that specifically represents the <iframe>, so you need to use the HtmlGenericControl. (In Visual Studio .NET, just right-click the control, and choose Run As Server Control).

Now you can set the src attribute at any point to redirect the frame:

IFrame1.Attributes["src"] = "page.aspx";

Of course, you can’t actually interact with the page objects of the embedded page. In fact, the page isn’t even generated in the same pass. Instead, the browser will request the page referenced by the src attribute separately and then display it in the frame. However, you can use a variety of techniques for passing information between the pages, including session state and the query string.

Figure 29-18 shows a page with two embedded frames, one of which has a border. The topmost <iframe> is using the page processor from earlier in this chapter, which indicates to the user that a part of the page is still being processed.

Figure 29-18. Using inline frames

1010 C H A P T E R 2 9 J AVA S C R I P T

Summary

In this chapter, you saw how a bit of carefully chosen JavaScript code can extend your ASP.NET web pages with more responsive interfaces and more dynamic effects. Along the way, you saw how to develop .NET solutions for some traditional HTML and JavaScript techniques, such as page processors, pop-up windows, rollover buttons, and frames. You also explored the new client callback feature that helps you implement seamless page updates. You can do a lot more by creatively applying a little JavaScript. For more ideas, check out some of the custom controls available at Microsoft’s http://www.asp.net community website.

C H A P T E R 3 0

■ ■ ■

Dynamic Graphics and GDI+

In Chapter 4, you learned about basic web controls for displaying graphics, such as the Image and ImageButton controls. Both allow you to display an image, and the ImageButton control also fires a Click event that gives you the exact mouse coordinates. But in a modern web application, you’ll often want much more.

In this chapter, you’ll learn about two .NET innovations that give you greater control over the look and feel of your website. First, you’ll learn about the ImageMap control, which allows you to define invisible shaped regions over an image and react when they’re clicked. Next you’ll tackle GDI+, a .NET model for rendering dynamic graphics. You’ll learn how to render custom graphics with GDI+, how to embed these graphics in a web page, and how to create custom controls that use GDI+.

Note Almost all of the techniques discussed in this chapter are equally well supported in ASP.NET 1.x. The key addition is the ImageMap control, which provides a server control wrapper for the <map> and <area> tags.

The ImageMap Control

Web pages commonly include complex graphics, where different actions are taken depending on what part of the graphic is clicked. ASP.NET developers can use several tricks to implement this design:

Stacked image controls: Multiple borderless pictures will look like one graphic when carefully positioned next to each other. You can then handle the clicks of each control separately. This approach works well for buttons and navigational controls that have defined, rectangular edges.

ImageButton: When an ImageButton control is clicked, it provides the coordinates where the click was made. You can examine these coordinates in your server-side code and determine what region was clicked programmatically. This technique is flexible but tedious and errorprone to code.

ImageMap: With the ImageMap control, you can define separate regions and give each one a unique name. One advantage of this approach is that as the user moves the mouse pointer over the image, it changes to a hand only when the user is positioned over a defined region. Thus, this approach works particularly well for detailed images that have small hotspots.

The ImageMap control is new in ASP.NET 2.0. It provides a server-side abstraction over the HTML <map> and <area> tags, which define an image map. The ImageMap control renders itself as

1011

1012 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 +

a <map> tag. You define regions by adding HotSpot objects to the ImageMap.HotSpots collection, and each region is rendered as an <area> tag inside the <map> tag. Just before the <map> tag, ASP.NET renders the linked <img> tag that shows the picture and uses the image map.

For example, if you create a map named ImageMap1 with three circular hotspots, the ImageMap control will render markup like this:

<img id="ImageMap1" src="cds.jpg" usemap="#ImageMapImageMap1" style="border-width:0px;" />

<map name="ImageMapImageMap1">

<area shape="circle" coords="272,83,83" href="javascript:__doPostBack('ImageMap1','0')" title="DVDs" alt="DVDs" /> <area shape="circle" coords="217,221,83" href="javascript:__doPostBack('ImageMap1','1')" title="Media" alt="Media" /> <area shape="circle" coords="92,173,83" href="javascript:__doPostBack('ImageMap1','2')" title="CDs" alt="CDs" />

</map>

Creating Hotspots

You can add an ImageMap control to a form in much the same way as an Image control. Just drop it onto the page, and set the ImageUrl property to the name of the image file you want to use. You can also use the usual ImageAlign, BorderStyle, BorderWidth, and BorderColor properties.

To define the clickable regions, you need to add HotSpot objects to the ImageMap.HotSpots property. You can use three derived classes: CircleHotSpot, RectangleHotSpot, and PolygonHotSpot. These choices aren’t arbitrary—they match the three shape types defined in the HTML standard.

Before you can tackle this task, you need to know the exact coordinates of the hotspot you want to create. Unfortunately, the ImageMap designer isn’t much help, so you’ll probably rely on a dedicated HTML authoring program. For example, Figure 30-1 shows three circle hotspots being adjusted with Microsoft FrontPage.

Figure 30-1. Configuring hotspots in Microsoft FrontPage

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 +

1013

Tip It’s acceptable to have overlapping hotspots, but the hotspot that is defined first will handle the click. In the example shown in Figure 30-1, this means it makes sense to define the hotspots in this order: DVDs, Media, CDs.

Once you’ve tweaked the hotspots to perfection, you can look at the source code to find the coordinates. In the case of a circle, three details are important: the X coordinate, Y coordinate, and radius. They appear in that order in the <area> tag:

<area shape="circle" coords="272, 83, 83" ...>

This tag defines the hotspot around the DVD region. The circle’s center is at (272, 83), and the radius is 83 pixels.

When defining a rectangle, you define the top-left and bottom-right corners. The order of coordinates is left X, top Y, right X, and bottom Y. When defining a polygon, you can have as many points as you like. The browser draws a line from one point to another to create the shape. You list the X and Y coordinates for your points in pairs like this: X1, Y1, X2, Y2, X3, Y3, and so on. It’s recommended (according to the HTML standard) that you end with the same point with which you started.

Once you’ve determined your hotspots, you can add the corresponding HotSpot objects. Here’s the ImageMap for Figure 30-1, with three hotspots:

<asp:ImageMap ID="ImageMap1" runat="server" ImageUrl="~/cds.jpg"> <asp:CircleHotSpot AlternateText="DVDs"

Radius="83" X="272" Y="83" /> <asp:CircleHotSpot AlternateText="Media" Radius="83" X="217" Y="221" /> <asp:CircleHotSpot AlternateText="CDs" Radius="83" X="92" Y="173" />

</asp:ImageMap>

Rather than coding this by hand, you can select your ImageMap and click the ellipsis next to the HotSpots property in the Properties window. This opens a collection editor where you can add and modify each hotspot.

Once you’ve defined the hotspots, you can test them in a browser. When you move the mouse pointer over a hotspot, it changes into a hand. You’ll also see that the alternate text you’ve defined for the hotspot appears in a tooltip.

Handling Hotspot Clicks

The next step is to make the hotspots clickable. A hotspot can trigger one of two actions—it can navigate to a new page, or it can post back your page (and fire the ImageMap.Click event). To choose which option you prefer, simply set the ImageMap.HotSpotMode property.

Tip When you set the ImageMap.HotSpotMode property, it applies to all hotspots. You can also override this setting for individual hotspots by setting the HotSpot.HotSpotMode property. This allows you to have some hotspots that post back the page and others that trigger page navigation.

To disable hotspots completely, use HotSpotMode.Inactive. If you use HotSpotMode.Navigate, you need to set the URL for each hotspot using the HotSpot.NavigateUrl property. If you use HotSpotMode.PostBack, you should give each hotspot a unique HotSpot.PostBackValue. This allows you to identify which hotspot triggered the postback in the Click event.

1014 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 +

Here’s the revised ImageMap control declaration that adds these details:

<asp:ImageMap ID="ImageMap1" runat="server" ImageUrl="~/cds.jpg" HotSpotMode="PostBack" OnClick="ImageMap1_Click"> <asp:CircleHotSpot AlternateText="DVDs" PostBackValue="DVDs"

Radius="83" X="272" Y="83" />

<asp:CircleHotSpot AlternateText="Media" PostBackValue="Media" Radius="83" X="217" Y="221" />

<asp:CircleHotSpot AlternateText="CDs" PostBackValue="CDs" Radius="83" X="92" Y="173" />

</asp:ImageMap>

Here’s the Click event handler, which simply displays the name of the clicked hotspot:

protected void ImageMap1_Click(object sender, ImageMapEventArgs e)

{

lblInfo.Text = "You clicked " + e.PostBackValue;

}

Figure 30-2 shows the resulting page.

Figure 30-2. Handling a hotspot click

A Custom Hotspot

The ImageMap control supports any HotSpot-derived hotspot class. ASP.NET includes exactly three, which correspond to the three basic types of <area> shapes defined in the HTML standard. However, you can create your own hotspots by deriving your own custom class from HotSpot.

Obviously, a custom hotspot class can’t do anything that falls outside the HTML standard. For example, it would be nice to have an ellipse and other curved shapes, but that just isn’t available.

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 +

1015

However, you can create a variety of complex multisided shapes, such as triangles, octagons, diamonds, and so on, using the polygon type. By deriving a custom HotSpot, you can create a higher-level model that generates the appropriate polygon based on a few basic pieces of information (such as the center coordinate and the radius).

For example, the following class presents a simple custom triangle. This triangle is created based on a center point, width, and height.

public class TriangleHotSpot : HotSpot

{

public TriangleHotSpot()

{

Width = 0;

Height = 0; X = 0;

Y = 0;

}

public int Width

{

get { return (int)ViewState["Width"]; } set { ViewState["Width"] = value; }

}

public int Height

{

get { return (int)ViewState["Height"]; } set { ViewState["Height"] = value; }

}

// X and Y are the coordinates of the center point. public int X

{

get { return (int)ViewState["X"]; } set { ViewState["X"] = value; }

}

public int Y

{

get { return (int)ViewState["Y"]; } set { ViewState["Y"] = value; }

}

...

When creating a custom HotSpot, you must override the MarkupName property to return the type of shape you are creating. Remember, the only valid choices are circle, rectangle, and polygon. This information is placed into the shape attribute of the <area> tag.

...

protected override string MarkupName

{

get { return "polygon"; }

}

...

Finally, you need to override the GetCoordinates() method to return the string for the cords attribute. For a polygon, this must be a comma-separated series of points in X, Y pairs. Here’s the code that creates a simple triangle, with a bottom edge and a single point in the top center:

1016 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 +

...

public override string GetCoordinates()

{

//Top coordinate. int topX = X;

int topY = Y - Height / 2;

//Bottom-left coordinate. int btmLeftX = X - Width / 2; int btmLeftY = Y + Height / 2;

//Bottom-right coordinate. int btmRightX = X + Width / 2; int btmRightY = Y + Height / 2;

return topX.ToString() + "," + topY.ToString() + "," + btmLeftX.ToString() + "," + btmLeftY.ToString() + "," + btmRightX.ToString() + "," + btmRightY.ToString();

}

}

Now you can use your custom hotspot much as you use a custom control. The first step is to register a tag prefix for your namespace, as shown here:

<%@ Register TagPrefix="chs" Namespace="CustomHotSpots" %>

And here’s an ImageMap that uses the TriangleHotSpot:

<asp:ImageMap ID="ImageMap1" runat="server" ImageUrl="~/triangle.gif"> <chs:TriangleHotSpot AlternateText="Triangle"

X="140" Y="50" Height="75" Width="85" /> </asp:ImageMap>

Drawing with GDI+

GDI+ is an all-purpose drawing model for .NET applications. GDI+ has a number of uses in .NET, including writing documents to the printer, displaying graphics in a Windows application, and rendering graphics in a web page.

Using GDI+ code to draw a graphic is slower than using a static image file. However, it gives you much more freedom and enables several possibilities that weren’t possible (or were prohibitively difficult) in earlier web development platforms, such as classic ASP. For example, you can create rich graphics that incorporate user-specific information, and you can render charts and graphs on the fly based on the records in a database.

The heart of GDI+ programming is the System.Drawing.Graphics class. The Graphics class encapsulates a GDI+ drawing surface, whether it is a window, a print document, or an in-memory bitmap. ASP.NET developers rarely have the need to paint windows or print documents, so it’s the last option that is the most practical.

To use GDI+ in ASP.NET, you need to follow a sequence of four steps:

1.Create the in-memory bitmap where you’ll perform all your drawing.

2.Create a GDI+ graphics context for the image. This gives you the System.Drawing.Graphics object you need.

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 +

1017

3.Perform the drawing using the methods of the Graphics object. You can draw and fill lines and shapes, and you can even copy bitmap content from existing files.

4.Write the binary data for the image to the browser, using the Response.OutputStream property.

In the following sections, you’ll see several examples of web pages that use GDI+. Before continuing, you may want to ensure that the following namespaces are imported:

using System.Drawing;

using System.Drawing.Drawing2D; using System.Drawing.Imaging;

The System.Drawing namespace defines many of the fundamental ingredients for drawing, including pens, brushes, and bitmaps. By default, Visual Studio adds using statements that import these namespaces to all your web pages. The System.Drawing.Drawing2D namespace adds other useful details such as the flexible GraphicsPath class, while System.Drawing.Imaging includes the ImageFormat namespace that lets you choose the graphics format in which your bitmap will be rendered when it’s sent to the client.

Simple Drawing

The following example demonstrates the simplest possible GDI+ page. All the work is performed in the event handler for the Page.Load event.

The first step is to create the in-memory bitmap by creating an instance of the System.Drawing.Bitmap class. When you create this object, you need to specify the height and width of the image in pixels as constructor arguments. You should make the size as small as possible. Not only will a larger bitmap consume additional server memory while your code is executing, but the size of the rendered content you send to the client will also increase, slowing down the transmission time.

//Create the in-memory bitmap where you will draw the image.

//This bitmap is 300 pixels wide and 50 pixels high.

Bitmap image = new Bitmap(300, 50);

The next step is to create a GDI+ graphics context for the image, which is represented by the System.Drawing.Graphics object. This object provides the methods that allow you to draw content on the in-memory bitmap. To create a Graphics object from an existing Bitmap object, you just use the static Graphics.FromImage() method, as shown here:

Graphics g = Graphics.FromImage(image);

Now comes the interesting part. Using the methods of the Graphics class, you can draw text, shapes, and image on the bitmap. In this example, the drawing code is exceedingly simple. It fills the graphic with a solid white background using the FillRectangle() method of the Graphics object.

//Draw a solid white rectangle.

//Start from point (1, 1).

//Make it 298 pixels wide and 48 pixels high. g.FillRectangle(Brushes.White, 1, 1, 298, 48);

The FillRectangle() method requires several arguments. The first argument sets the color, the next two parameters set the starting point, and the final two parameters set the width and height. When measuring pixels, the point (0, 0) is the top-left corner of your image in (X, Y) coordinates.

The X coordinate increases as you go farther to the right, and the Y coordinate increases as you go farther down. In the current example, the image is 300 pixels wide and 50 pixels high, which means the point (300, 50) is the bottom-right corner.