Using Flash for .NET and XML to Create Animated Site Navigation Components

Applies To:

Flash for .NET

Author:

John Juback

Published On:

8/11/2006

ComponentOne Flash for .NET (a.k.a. C1Flash) provides an object model for generating static images, slide shows, and animated movies in the popular Macromedia Flash file format (SWF). This article demonstrates how to implement a derived class, C1FlashGallery, that reads site navigation data from an XML file and converts it into a Flash movie patterned after the Spotlight sidebar on ESPN.com. The accompanying sample project (for Visual Studio 2005) also illustrates the use of the C1WebFlash component for rendering Flash content on an ASP.NET 2.0 page.

The Macromedia Flash file format (SWF) delivers vector graphics and animation over the Internet to the Macromedia Flash Player. The SWF file format is designed to be a very efficient delivery format, not a format for exchanging graphics between graphics editors. It is designed to meet the following goals - goals with which some other file formats cannot compete:

On-screen display

The format is primarily intended for on-screen display and supports anti-aliasing, fast rendering to a bitmap of any color format, and animation.

Extensibility

The format is a tagged format, so it can evolve with new features while maintaining backward compatibility with earlier versions of Flash Player.

Network delivery

The format can travel over a network with limited and unpredictable bandwidth.

Simplicity

The format is simple so that Flash Player is small and easily ported.

Scalability

The files work well on limited hardware, and can take advantage of better hardware when it is available. This is important because computers have different monitor resolutions and bit depths.

Speed

The files render at a high quality very quickly.

For more information on the Flash format and Flash utilities from Adobe (which acquired Macromedia) and other sources, visit the following link:

[www.adobe.com/flash](http://www.adobe.com/flash)

C1Flash provides most of the graphical drawing capabilities that the SWF format supports. For .NET developers, a key feature of C1Flash is ease of use. When using the C1FlashCanvas component, for example, the methods and properties for drawing graphics are patterned after the familiar .NET Graphics class. You do not need to know the details of the SWF format, as C1Flash provides RenderToFile and RenderToStream methods that generate the appropriate tags transparently. In other words, if you know how to use the Graphics class to draw in .NET, you already know how to use C1Flash.

C1Flash contains three components for generating SWF content:

C1FlashCanvas

This component is similar to the .NET Graphics class. It provides methods for drawing content to a single frame, or canvas, of Flash. C1FlashCanvas uses a logical pixel coordinate system.

C1FlashSlide

This component is used to create a slide show in the Flash file format. Each page of the slide is an instance of the FPage class, which is also the base class for C1FlashCanvas. A powerful Slide Designer makes it easy to specify navigation elements such as buttons, headers and footers, and page numbers.

C1FlashMovie

This component is used to create multi-frame animations, where each frame is composed of graphical shapes and buttons that derive from the FObject class. C1FlashMovie uses a coordinate system based on twips, where one twip is 1/20th of a logical pixel. A logical pixel is the same as a screen pixel when the movie is played at 100% - that is, without scaling.

For rendering Flash content in ASP.NET pages, C1Flash includes a single component:

C1WebFlash

This component provides a FlashSource property that accepts the name of an instance of a C1FlashCanvas, C1FlashSlide, or C1FlashMovie component as a content source. Alternatively, the MovieName property can be used to reference an existing SWF file.

When a C1WebFlash control is rendered, the appropriate or tag is inserted into the page, along with a URL reference to a physical SWF file. The associated FlashSource component saves its content into a temporary folder located within the Web application's virtual path, using a filename that matches the generated HTML tag. These temporary SWF files are removed according to the time span specified by the SlidingExpiration property of the C1WebFlash control.

The code sample discussed in this article derives the C1FlashGallery class from C1FlashMovie and displays an instance of that class in an ASP.NET page using the C1WebFlash component.

Working with Frames

Flash animations are composed of a series of frames. To create an animation with C1FlashMovie, you create graphical objects derived from the FObject class and add them to a particular frame. To move, scale, or rotate an object, you remove it from a subsequent frame, apply the desired transformation, then re-add it to that frame or one that follows it. When you are finished populating the frames, you can call the RenderToFile method to generate the SWF file.

The trickiest part of the implementation is keeping track of which objects reside on which frames. Once you add an object to a given frame, it remains on all subsequent frames until you explicitly remove it. So, in order to create a marquee effect with a string moving right to left, you would have to create an FText (or FEditText) object to hold the string, add it to the first frame, remove it from the second frame, shift it left, add it to the second frame, remove it from the third frame, shift it left, add it to the third frame, and so on. Here is a simplified example in C#:

C1FlashMovie movie = new C1FlashMovie();  
movie.Width = 4000;  
movie.Height = 1000;  

string msg = "Colorless green ideas sleep furiously";  
Rectangle rect = new Rectangle(4000, 0, 4200, 1000);  
FEditText text = new FEditText(rect, msg, "Arial");  

text.ReadOnly = true;  
text.NoSelect = true;  
text.WordWrap = false;  

for (int i = 0; i < 100; i  )  
{  
    text.Translate(-i * rect.Right / 100, 0);  
    movie.Frames(i).AddObject(text);  
    movie.Frames(i   1).RemoveObject(text);  
}

In this example, the Rectangle dimensions are specified in twips as per the Macromedia SWF specification, and the width is hard-coded to approximate the length of the message string in Arial 10pt. In a real application, you would measure the string first, as we shall demonstrate later on.

Even though the message string does not need to be editable, we typically use the FEditText class instead of FText, as it exposes a several properties for customization, such as WordWrap. Setting ReadOnly and NoSelect to true effectively makes an FEditText object behave like a static label.

Note that the Frames collection of the C1FlashMovie class is nonstandard in the sense that it does not support the usual Add/Remove methods and Count property. The act of referencing a frame by its zero-based index number implicitly creates that frame if it does not already exist. Although such side effects may be counterintuitive, this behavior comes in handy for quickly replicating frames while ensuring that background objects carry over from one frame to the next.

Therefore, the preceding example creates a movie with 101 frames (index numbers 0 through 100). Note that the final frame is empty, since the only operation performed on it is a call to the RemoveObject method. For a scrolling marquee, this is perfectly acceptable. If the movie plays in a continuous loop, the empty frame adds a bit of padding prior to restarting the animation. If the movie plays just once, ending with an empty frame prevents any trailing pixels of the last animation frame from being accidentally displayed.

In the most recent version of C1Flash (2.0.20063.44 at the time of this writing), there is no way to delete a frame once it has been created. Therefore, if you want to ensure that the final frame contains the object(s) being animated, you must take care not to implicitly create the final frame by calling RemoveObject. In the preceding example, this can be done by amending the last statement as follows:

if (i   1 < 100)  
    movie.Frames(i   1).RemoveObject(text);

Finally, note the use of the Translate method to change the position of the text. All objects that derive from FShape, such as FEditText, support this method. Although not covered in this article, additional animation effects are available via the Scale and Rotate methods.

Working with Objects in Frames

The abstract class FShape is the base class for geometric shapes, text, and images used to compose animation frames. The following is a list of classes that derive from FShape:

FArc

Represents an arc, pie slice, or chord.

FBeziers

Represents a Bezier curve.

FCircle

Represents a circle. Inherits from FOval.

FEditText

Dynamic text with support for alignment, margins, indentation, multiple lines, word wrapping, and simple HTML tags.

FImage

Represents a System.Drawing.Image object. Inherits from FRectangle.

FLine

Represents a straight line.

FOval

Represents an oval.

FPath

Represents a System.Drawing.Drawing2D.GraphicsPath object.

FPolygon

Represents a polygon.

FRectangle

Represents a rectangle.

FText

Static text.

Each of these classes inherits common attributes from FShape, such as line color and width, opacity, and fill methods. The code sample discussed in this article uses the following shape classes: FArc, FCircle, FEditText, FImage, and FRectangle.

Each graphical object needs to be assigned a depth value that determines the stacking order of the object. Objects with lower depth values are displayed underneath objects with higher depth values. An object with a depth value of 1 is displayed at the bottom of the stack. An object may appear more than once in the frame, but at different depths. Within a single frame, there can be only one object at any given depth.

The FButton class associates user actions with a shape. Its constructor accepts four shapes (which can be the same object) representing the up, over, down, and hit states. The AddAction method specifies both a mouse event and an action record, which is used to implement navigation commands, URL references, and other primitive operations.

The FGroup class acts as a container for FObject instances, which simplifies the task of adding/removing multiple objects to/from individual frames.

To quote Igor Stravinsky, "Lesser artists borrow, great artists steal." I stole the idea for this article from the Spotlight animation implemented in Macromedia Flash on the ESPN.com web site. Here is a snapshot of the ESPN version:

The title bar at the top contains a handful of textual links. Clicking a link displays the associated content for that category, which consists of clickable thumbnail images and synopses that navigate to an article on the site. If the reader does not interact with the page, the animation activates the next category link after pausing for a few seconds, cycling back to the beginning after the last one expires. The light gray ring in the upper right corner gradually fills with a darker shade of gray, serving as a timer for the current view. Clicking the ring stops or resumes automatic playback.

Except for the color scheme, content, and the appearance of the animated circle in the title bar, the C1FlashGallery implementation looks almost identical:

Designing the Frame Layout

Before you start coding, it is a good idea to make a spreadsheet to keep track of the frame numbers, the objects on each frame, and the depth of the objects. This will help you to organize your code and identify common elements that can be generated by the same routine.

In the C1FlashGallery sample depicted in the previous section, the following objects are added to the first frame (index number 0) and remain there for the duration of the movie. They are listed in increasing depth order:

  • Title bar background (gray with border)
  • Title bar caption (black text with chevron)
  • Inactive button backgrounds (medium gray rectangles, one for each category)
  • Button captions (white underlined text, one for each category)
  • Circle timer background (initial state, white interior)

For each category, an FGroup is created. The following objects, listed in increasing depth order, are added to each FGroup:

  • Active button background (red rectangle)
  • Thumbnail images (one for each item)
  • Thumbnail caption (one for each item)

Each FGroup remains active for the same number of frames (120). The frame rate is set to 20 per second, so each category displays for 6 seconds. The first FGroup is added to frame 0, then removed from frame 120. The second FGroup is added to frame 120, then removed from frame 240. The third and final FGroup is added to frame 240 and remains there for the duration of the movie.

The circle timer is rendered by adding and removing arcs, drawn in increments of 3 degrees, to the frames between the FGroups, and the frames following the last FGroup. Note that the duration of each FGroup (120 frames) was derived by dividing the number of degrees in a circle (360) by the arc increment (3).

In our example, the following table shows the relationship between zero-based frame numbers and dynamic objects:

0

Demos: Active button background

0

Demos: Thumbnail images

0

Demos: Thumbnail captions

1 - 119

Circle timer arcs

120

Charts: Active button background

120

Charts: Thumbnail images

120

Charts: Thumbnail captions

121 - 239

Circle timer arcs

240

AJAX: Active button background

240

AJAX: Thumbnail images

240

AJAX: Thumbnail captions

241 - 359

Circle timer arcs

The following table shows the Depth property values for all objects, both static and dynamic:

1

Title bar background

2

Title bar caption

100

Demos: Inactive button background

101

Charts: Inactive button background

102

AJAX: Inactive button background

200

Demos: Active button background

200

Charts: Active button background

200

AJAX: Active button background

300

Demos: Button caption

301

Charts: Button caption

302

AJAX: Button caption

400 - 402

Thumbnail images

500 - 502

Thumbnail captions

1000

Circle timer background

1001

Circle timer arcs

Note that the category buttons are drawn in three layers. The bottommost layer is the inactive (medium gray) background. It is this layer that actually implements the "hot spot" for navigation. The middle layer is the active (red) background, and the top layer is the text, which is drawn with a transparent background (Opacity = 0).

Using XML as a Data Source

In order to facilitate content changes, all source text, image paths, and URLs are stored in an XML file. This approach offers significant advantages:

  • Nontechnical personnel can modify the movie by editing the XML file in Notepad. No coding is required.
  • The ASP.NET page can compare the time stamps of the XML and SWF files and only regenerate the movie when the XML file has changed.
  • The C1FlashGallery class translates the XML data into the appropriate FObject derivatives and populates the Frames collection accordingly. Consumers of this class need not know anything about the inner workings of the C1FlashMovie object model.
  • The data model can be easily extended by adding new attributes to existing tags.

The structure of the data file C1FlashGallery.xml is as follows (text abbreviated):



    <item  
     image="http://www.componentone.com/newimages/democenter\_net2\_ft_b.gif"  
     text="The AJAX Feature Tour highlights the new functionality..."  
     url="http://www.componentone.com/c1webdemo2"/>  
    <item  
     image="http://www.componentone.com/newimages/democenter_liveexamples.gif"  
     text="View live examples for .NET and ASP.NET 2.0 components..."  
     url="http://www.componentone.com/liveexamples"/>  
    <item  
     image="http://www.componentone.com/newimages/democenter_starterkit2.gif"  
     text="The C1 Movie Collection Starter Kit improves upon the original..."  
     url="http://www.componentone.com/starterkit2"/>  


    <item  
     image="http://www.componentone.com/newimages/chartnet\_01\_lg.jpg"  
     text="Use the 2D Chart to display data as a bar, X-Y plot, doughnut..."  
     url="http://www.componentone.com/chart"/>  
    <item  
     image="http://www.componentone.com/newimages/chartnet\_02\_lg.jpg"  
     text="Use the 3D Chart to create 3D surface, contour, or bar charts..."  
     url="http://www.componentone.com/chart"/>  


    <item  
     image="http://helpcentral.componentone.com/c1kb/upload/webgridexpanded.bmp"  
     text="Learn how C1 ASP.NET 2.0 components use AJAX..."  
     url="http://helpcentral.componentone.com/article.aspx?id=1506"/>  


Each tag represents a clickable button in the title bar. The name attribute specifies the button text.

Each within a group represents an article reference with the following attributes:

image

The path to the original image, which can be an absolute URL as in the preceding listing, or a partial path relative to the application's virtual root. The original image can be any size, as the C1FlashGallery class handles thumbnail creation based on the number of items in a group (at least one but typically not more than three).

text

The caption string that appears below the thumbnail image.

url

The URL (absolute or relative) to be visited when the user clicks either the thumbnail or its caption.

Reading the XML Data File

The C1FlashGallery class implements a public method, LoadFromXml, that translates XML data files into Flash animation frames. Most of the actual work is done by several private methods that either add objects to the Frames collection directly or populate FGroup objects that are added as a unit. The code for this method is as follows:

public void LoadFromXml(string filename)  
{  
    // Clear the movie and create static items that appear on all frames  
    this.Clean();  
    CreateTitleBar();  

    // Read the contents of the XML file into a DataSet  
    string xml = File.ReadAllText(filename);  
    StringReader reader = new StringReader(xml);  
    DataSet ds = new DataSet();  
    ds.ReadXml(reader);  

    // Add a column to the "group" table for storing Flash objects  
    DataTable groups = ds.Tables["group"];  
    groups.Columns.Add("objects", typeof(FGroup));  

    // Populate the Frames collection for each data row  
    int count = groups.Rows.Count;  

    for (int i = 0; i < count; i  )  
    {  
        // Add an empty Flash group to each data row  
        FGroup flashGroup = new FGroup();  
        DataRow group = groups.Rows[i];  
        group["objects"] = flashGroup;  

        // Create a button in the title bar  
        CreateButton(group, i);  

        // Create thumbnails, text, and navigation links for each item  
        CreateItem(group);  

        // Add the group to the appropriate frame  
        this.Frames(i * _spinFrames).AddGroup(flashGroup);  

        // Unless this is the last data row, remove the Flash group  
        // from the first frame belonging to the next XML group  
        if (i < count - 1)  
            this.Frames((i   1) * _spinFrames).RemoveGroup(flashGroup);  
    }  

    // Create spinning animation frames for each group  
    CreateSpinner(count);  
}

The XML file is converted to a DataSet containing a group table with one row for each category. For convenience, an objects column of type FGroup is added to this table. That way, we can pass around DataRow objects and use them to reference the associated FGroup container along with the XML data. After calling the private methods CreateButton and CreateItem to populate the FGroup, we add it to and remove it from the appropriate frames.

Finally, we call the private method CreateSpinner to animate the circle timer. When LoadFromXml is finished executing, the movie is ready to be rendered.

Creating Static Movie Elements

The private method CreateTitleBar creates the title bar background, caption, and the initial state of a spinning shape that stops the movie when clicked. All of these elements are added to the first frame (index number 0). The code for this method is as follows:

private void CreateTitleBar()  
{  
    // Reset current x-position in title bar  
    \_twipPosition = \_itemSpacing * Constants.TWIPS / 2;  

    // Create a solid rectangle with a border for the title bar  
    int width = (_movieWidth - 1) * Constants.TWIPS;  
    int height = _titleHeight * Constants.TWIPS;  
    FRectangle flashRect = new FRectangle(0, 0, width, height);  
    flashRect.LineColor = _titleLineColor;  
    flashRect.FillSolidColor(_titleBackColor);  

    // Make this the bottommost object for all frames  
    flashRect.Depth = _depthTitleBar;  
    this.Frames(0).AddObject(flashRect);  

    // Measure the size of the title string  
    Size szText = C1FlashCanvas.MeasureString(\_title, \_titleFont).ToSize();  

    // Compute the dimensions of the text rectangle  
    int y = (flashRect.Bounds.Height - szText.Height) / 2;  
    Rectangle r = new Rectangle(_twipPosition, y, szText.Width, szText.Height);  

    // Advance the starting x-position for the first button  
    \_twipPosition  = szText.Width   (\_itemSpacing * Constants.TWIPS);  

    // Create a readonly Flash text box for the title string  
    FEditText flashText = new FEditText(r, \_title, \_titleFont.Name, false, true);  
    flashText.ReadOnly = true;  
    flashText.NoSelect = true;  
    flashText.Height = 11 * Constants.TWIPS;  
    flashText.AutoSize = true;  
    flashText.ForeColor = _foreColor;  
    flashText.WordWrap = false;  
    flashText.Opacity = 0;  

    // Add the Flash text box to all frames  
    flashText.Depth = _depthTitleText;  
    this.Frames(0).AddObject(flashText);  

    // Compute the dimensions of the spinner rectangle  
    int sx = (\_movieWidth - \_itemSpacing - _spinSize - 1) * Constants.TWIPS;  
    int sy = (\_titleHeight - \_spinSize) * Constants.TWIPS / 2;  
    int radius = _spinSize * Constants.TWIPS / 2;  
    _spinRect = new Rectangle(sx, sy, radius * 2, radius * 2);  

    // Add the initial spinner shape (unfilled circle)  
    FCircle flashCircle = new FCircle(sx   radius, sy   radius, radius);  
    flashCircle.LineColor = _spinColor;  

    // Create a stop button based on the circle  
    FButton flashStop = new FButton(flashCircle, flashCircle, flashCircle, flashCircle);  
    flashStop.AddAction(FButtonEvent.RELEASE, new FActionStop());  

    // Add the stop button to all frames  
    flashStop.Depth = _depthSpinnerBase;  
    this.Frames(0).AddObject(flashStop);  
}

Note the use of the static member function C1FlashCanvas.MeasureString to compute the actual width and height of the title string. This allows the buttons that follow it to be placed precisely regardless of the length of the text.

Also note the use of the AddAction method to associate a "stop animation" command with the circle timer. Since the SWF format does not provide a "toggle play/stop" primitive, the user must right-click the movie and choose Play from the shortcut menu to resume animation.

Creating Group Navigation Buttons

The private method CreateButton creates a clickable button that navigates to a group and highlights when the group is being shown during playback. The bottom and top layers of the button (unselected rectangle and transparent text) are added to frame 0, while the middle layer (highlight rectangle) is added to the FGroup object associated with this data row. The code for this method is as follows:

private void CreateButton(DataRow group, int index)  
{  
    // Measure the size of the button text  
    string name = group["name"].ToString();  
    Size szText = C1FlashCanvas.MeasureString(name, _buttonFont).ToSize();  

    // Compute the size of the button rectangle  
    int x = _twipPosition;  
    int y = _buttonMargin * Constants.TWIPS;  
    int width = szText.Width   (2 * _buttonMargin * Constants.TWIPS);  
    int height = _titleHeight * Constants.TWIPS;  

    // Create a solid rectangle for the inactive button background  
    FRectangle flashRect = new FRectangle(x, y, width, height - (2 * y));  
    flashRect.LineColor = _buttonBackColor;  
    flashRect.FillSolidColor(_buttonBackColor);  

    // Create a Flash button based on this rectangle for frame navigation  
    FButton flashButton = new FButton(flashRect, flashRect, flashRect, flashRect);  
    flashButton.AddAction(FButtonEvent.RELEASE, new FActionGotoFrame((ushort)(index * _spinFrames)));  

    // Add the Flash button to all frames  
    flashButton.Depth = _depthButtonBase   index;  
    this.Frames(0).AddObject(flashButton);  

    // Get the (empty) group of Flash objects for this data row  
    FGroup flashGroup = group["objects"] as FGroup;  

    // Create a solid rectangle for the active button background  
    FRectangle flashActiveRect = new FRectangle(x, y, width, height - (2 * y));  
    flashActiveRect.LineColor = _buttonActiveColor;  
    flashActiveRect.FillSolidColor(_buttonActiveColor);  

    // Add this rectangle to the Flash group for this data row  
    flashActiveRect.Depth = _depthButtonHighlight;  
    flashGroup.AddObject(flashActiveRect);  

    // Advance the x-position for the next button  
    \_twipPosition  = width   (\_itemSpacing * Constants.TWIPS);  

    // Compute the size of the text rectangle  
    x  = ((_buttonMargin   1) * Constants.TWIPS);  
    y = (height - szText.Height) / 2;  
    Rectangle r = new Rectangle(x, y, width, szText.Height);  

    // Create a readonly Flash text box for the button text  
    // (use HTML mode to achieve underlining)  
    string html = String.Format("**{0}**", name);  
    FEditText flashText = new FEditText(r, html, _buttonFont.Name, false, true);  
    flashText.Html = true;  
    flashText.Align = FAlignment.Center;  
    flashText.ReadOnly = true;  
    flashText.NoSelect = true;  
    flashText.AutoSize = true;  
    flashText.Height = 10 * Constants.TWIPS;  
    flashText.ForeColor = _buttonForeColor;  
    flashText.Opacity = 0;  

    // Add the button text to all frames (above button rectangles)  
    flashText.Depth = _depthButtonTextBase   index;  
    this.Frames(0).AddObject(flashText);  
}

Note the use of the Html property on the FEditText object. Since the SWF format only handles bold and italic font attributes, we use basic HTML tags to achieve underlining for the button captions to make them recognizable to users as textual links.

The private method CreateItem creates clickable thumbnails and text links for the articles in the specified group. All objects are added to the FGroup container associated with this data row. Note the use of the GetChildRows method to obtain an array of DataRow objects corresponding to the tags within the current . The code for this method is as follows:

private void CreateItem(DataRow group)  
{  
    // Get the array of items for this group row  
    DataRow[] items = group.GetChildRows("group_item");  

    // Compute the target width based on the number of items  
    int itemWidth = (\_movieWidth - ((items.Length - 1) * \_itemSpacing)) / items.Length;  

    // Get the group of Flash objects for this data row  
    FGroup flashGroup = group["objects"] as FGroup;  

    // For each item, create image/text buttons and add them to the Flash group  
    for (int i = 0; i < items.Length; i  )  
    {  
        // Get strings from the item's data row  
        string name = items[i]["image"].ToString();  
        string url = items[i]["url"].ToString();  
        string text = items[i]["text"].ToString();  

        // If no URL is given, use the image path  
        if (url.Length == 0)  
            url = name;  

        // Compute the rectangle for the thumbnail image  
        int x = i * Constants.TWIPS * (itemWidth   _itemSpacing);  
        int y = _itemTop * Constants.TWIPS;  
        int width = itemWidth;  
        int height = width * 3 / 4;  

        // If there is only one item, reduce the height to make room for text  
        if (items.Length == 1)  
            height = height * 3 / 5;  

        // Load the image at the desired size  
        System.Drawing.Image image = LoadAndResizeImage(name, ref width, ref height);  

        // Convert the size to twips  
        width *= Constants.TWIPS;  
        height *= Constants.TWIPS;  

        // Create a Flash image object  
        Rectangle r = new Rectangle(x, y, width, height);  
        FImage flashImage = new FImage(r, image);  

        // Create a Flash button based on the image and the target URL  
        FButton flashImageButton = new FButton(flashImage, flashImage, flashImage, flashImage);  
        flashImageButton.AddAction(FButtonEvent.RELEASE, new FActionGetURL(url));  

        // Add the button to the item's Flash group  
        flashImageButton.Depth = _depthItemImageBase   i;  
        flashGroup.AddObject(flashImageButton);  

        // Compute the rectangle for the text below the image  
        x  = _itemSpacing * Constants.TWIPS;  
        y  = height   (_itemSpacing * Constants.TWIPS);  
        width -= _itemSpacing * Constants.TWIPS;  
        height = (_movieHeight * Constants.TWIPS) - y;  
        r = new Rectangle(x, y, width, height);  

        // Create two Flash text boxes, one normal, one for mouseover  
        FEditText[] flashArray = new FEditText[2];  

        for (int n = 0; n < 2; n  )  
        {  
            flashArray[n] = new FEditText(r, text, _itemFont.Name);  
            flashArray[n].MultiLine = true;  
            flashArray[n].ReadOnly = true;  
            flashArray[n].NoSelect = true;  
            flashArray[n].AutoSize = true;  
            flashArray[n].Height = 11 * Constants.TWIPS;  
            flashArray[n].ForeColor = (n == 0) ? \_foreColor : \_hoverColor;  
            flashArray[n].Opacity = 0;  
        }  

        // Create a Flash button based on these text boxes  
        FButton flashTextButton = new FButton(flashArray[0], flashArray[1], flashArray[1], flashArray[0]);  
        flashTextButton.AddAction(FButtonEvent.RELEASE, new FActionGetURL(url));  

        // Add the button to the item's Flash group  
        flashTextButton.Depth = _depthItemTextBase   i;  
        flashGroup.AddObject(flashTextButton);  
    }  
}

A private method, LoadAndResizeImage, handles image URL references and resizing. For details, see the full source code.

The mouseover effect for text links is achieved by creating two FEditText objects with different colors, then assigning them to the appropriate button states.

Filling the Circle Timer

The private method CreateSpinner creates spinning animation frames for each group by repeatedly adding and removing arcs with an increasing angle measurement. The code for this method is as follows:

private void CreateSpinner(int count)  
{  
    for (int n = 0; n < count; n  )  
    {  
        for (int i = 1; i < _spinFrames; i  )  
        {  
            // Draw arcs starting at 12:00 in 3 degree increments  
            FArc a = new FArc(_spinRect, 270, 3 * i, true, false);  
            a.LineColor = _spinColor;  
            a.FillSolidColor(_spinColor);  
            a.Depth = _depthSpinnerArc;  

            // Add the arc to the appropriate frame  
            this.Frames((n * _spinFrames)   i).AddObject(a);  

            // Unless this is the final arc, remove it from the next frame  
            if (!(n == count - 1 && i == _spinFrames - 1))  
                this.Frames((n * _spinFrames)   i   1).RemoveObject(a);  
        }  
    }  
}

On the sample site, the default ASP.NET page contains a single C1WebFlash component with the following property settings:

Width

412px

Height

265px

The code behind for this page implements only the Page_Load event, the body of which is as follows:

if (!IsPostBack)  
{  
    string xmlPath = Page.MapPath("C1FlashGallery.xml");  
    string swfPath = Page.MapPath("C1FlashGallery.swf");  

    // Suppress licensing nag screen  
    C1FlashMovie movie = new C1FlashMovie();  

    // Create the Flash file if not found or if the XML file is more recent  
    if (!File.Exists(swfPath) || File.GetLastWriteTime(xmlPath) > File.GetLastWriteTime(swfPath))  
    {  
        C1FlashGallery gallery = new C1FlashGallery(this);  
        gallery.LoadFromXml(xmlPath);  
        gallery.RenderToFile(swfPath);  
    }  

    // Always use the last saved version of the Flash file  
    C1WebFlash1.MovieName = "C1FlashGallery.swf";  
}

Note that the C1FlashGallery component is only created if the generated SWF file is out of date, and that all of the internal C1FlashMovie logic is encapsulated in the LoadFromXml method.

The C1FlashGallery sample provides a model for generating data-driven animations using the C1FlashMovie component and related classes. You can use the sample project as a starting point for customizations such as generating multiple item rows and surfacing internal quantities as properties.

GrapeCity

GrapeCity Developer Tools
comments powered by Disqus