Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Comparing Flash and WPF

0.00/5 (No votes)
7 Nov 2009 1  
Comparing Flash and WPF.

Contents

Introduction

There has been a lot of talk lately about WPF/Silverlight. I have also been learning and writing WPF articles, but what I thought might make an interesting article would be to compare and contrast WPF and Flash.

I feel I have the right to do this, as I have worked with both technologies, and although I am not selling myself as an expert in either, I feel confident enough to write this article. I thought it would just make some interesting reading for others that just don't know how WPF or Flash work. For example, if you're a Flash developer and don't know what WPF is, I am hoping this article will help you, and vice versa.

Caveat

I should firstly mention that I only have Flash MX 2004, which I believe is Flash 7.0, which used the second generation of the Flash scripting language (Action Script 2, here after known as AS2). Since that, Macromedia was bought by Adobe, who have created Creative Studio 3 (CS3) which includes Flash under this general umbrella. Not only that, they have totally re-written the scripting side of things in Flash, and it is now Action Script 3 (here after known as AS3). I know nothing about AS3, except that it is Java like, and will allow OO type stuff like inheritance etc., but I do have an excellent Flash developer friend that assures me that writing about AS2 will not be a waste of time, as although it is different, the areas I am going to cover in this article are still the same. So I will stick to writing about AS2 if it is OK with your lot. After all, people are still writing about .NET 2.0, jeez that's so old man. .NET 3.0/3.5 are here, come on man, get with the program. No, only kidding, whatever floats your boat is OK by me. I write about what I like. Period. If others like it, great; if not, that is also fine, I will still carry on writing about stuff I like.

So let's compare WPF and Flash

I think the easiest way to start this is to just go through the features one at a time. I am specifically only picking on features that WPF introduced; otherwise, this would be a very, very long and tedious article. By this, I mean, I will not discuss threading, sockets, databases, Remoting, XML handling, specifically, though Flash is more than capable of all of these, with the exception of threading.

Below is a list of things that I think Flash / WPF people would like to know about each other's worlds:

  • Coding style
  • Controls
  • Layout
  • Resources
  • Databinding
  • Styles / Templates / Skins and Themes
  • 2D graphics / 3D graphics
  • Animations
  • Structuring applications
  • APIs

So shall we continue? What I am going to do is under each of the headings below, have a Flash view point followed by a WPF viewpoint; that way, people should be able to see what the various pros and cons are for themselves.

Coding style

Flash

Before we get on to how to code in Flash, it is fairly important to understand how Flash works at a conceptual level. So let's start there, shall we?

Flash is based on a library of resources which can be created on an initial stage (work area) and then grouped together to form either a Button/Graphic or a MovieClip. When created, these groupings of stage objects in one of the three objects just mentioned can be added to the library (more on this later) as resources. Later on, the library components can be brought to the stage either manually (dragged on to stage) or programmatically (the right way, in my opinion).

The stage is basically constructed of layers down and time across.

The stage allows a user to create the following:

  • Single frames: Which could contain objects from the library, or just normal 2D shapes, or even standard controls from the controls supplied with Flash
  • Motion tweens (more on this later, see the animations section)
  • Key frames: Are used to specify exactly what an object will look like by specifying exactly the properties of an object at that point in time
  • Guide layers: Are used to create motion paths for other objects on the stage
  • Mask layers: Are used to hide any object rendering that goes outside of the mask layer's bounds
  • Actions: May be applied to any key frame on any layer within the stage, or may be attached to a control or other library objects that exist on the stage, such as a MovieClip object, for example

For those of you that have never used Flash, it has its own language support, which is known as Action Script. The latest version of Flash (the one in Adobe CS3 product) uses Action Script 3 (AS3). I don't have access to that though, so I will be discussing things using Flash MX 2004, which uses AS2.

So what can we do with this Action Script stuff? Well, as it turns out, we can do quite a lot actually.

The way that Flash has developed Action Script is quite cool and what one would expect coming from an OO language. AS2 was not 100% OO, but it was a damn fine start. There was and still is no threading support, but apart from that, I think it is a nice set of classes and libraries/operators and keywords.

There are pre-built classes for working with MovieClips, sound, video, web cams, sockets, event handling, and also the usual collections, if-then-else, loops, math classes. We can subclass existing controls, create new controls and classes etc. It is quite mature actually, even better in the new AS3, which will be 100% OO.

So now that we know that we have these classes available, what can we do with them, and how do we trigger a bit of Action Script to run in the first place?

Well, that is actually very, very easy. All we need to do is create an Action of a frame or an object. An example is shown below, where I am creating an Action on Frame1 of the stage. When an Action exists for a key frame, a little "a" will be shown for that frame on the stage.

It can be seen from this small screenshot that we can create functions, and call methods on existing classes. And what is this first line doing?

attachManager = new AttachManager();

That kind of looks like it is creating an object, doesn't it? Well yes, that exactly is what it is doing. We can create objects in Flash Action Script that will bring to life external Action Script files and create objects from them. Shall we take a sneaky peak at what this external Action Script file looks like?

// \\\\\\\\\\\\\\
//
// AttachManager class : Allows movie to be added to the timeline using 
// scripting. The movies will be added using the linkage name within the 
// library. The attach method also returns the attached MovieClip to the 
// timeline. This class is instatiated from the _level0 timeline using action 
// script like
//
//      attachManager = new AttachManager()
//
//      var movie1 = attachManager.attach('movie1', 0, 0)
//
// \\\\\\\\\\\\\\\

class AttachManager{
    
    //instance fields
    private var attachArr:Array;
    private var i:Number;
    
    //constructor
    function AttachManager(){
        //holding array for all objects the manager holds
        attachArr = new Array();
    }
    
    //attach : Attaches the object to the timeline using the parameters 
    // provided
    //
    //PARAMETERS :
    //linkage:String : A string representing the linkage name for the object 
    // to attach
    //xpos:Number : The X-Pos to use for the object to attach
    //ypos:Number : The Y-Pos to use for the object to attach
    //depth : The depth to use, where the object will be attached. In action 
    //script depth goes from 0 downwards where as in flash depth goes from 0 
    // upwards
    //
    //RETURNS :
    //MovieClip : that may be used by the flash timeline via action script
    public function attach(linkage:String, xpos:Number, ypos:Number,depth):MovieClip {
        
        //get the depth if no depth exists yet        
        if(depth==undefined){
            
            depth = getCurDepth();
        }
        //now do the attaching to _level0, using the paraaddEventListenermeters 
        // porovided
        var tmpClip = _level0.attachMovie(linkage,linkage,depth,{_x:xpos,_y:ypos})
        tmpClip._name=linkage;
        //store the object in the holding array 
        attachArr.push(tmpClip)
        //return the newly attached MovieClip
        return tmpClip;
    }
    
    //getCurDepth : Get the HighestDepth for the timeline
    //
    //RETURNS :
    //Number : that represents the HighestDepth for the timeline
    public function getCurDepth():Number {
        //return the depth
        return _level0.getNextHighestDepth();
    }
    
    //killClip : Removes an object from the AttachManager  
    //
    //PARAMETERS :
    //clip : the object to remove from the internal holding array 
    public function killClip(clip) {
        //remove the clip
        _level0.removeMovieClip(clip);
        
    }
    
    //removeAllClips : Removes all objects from the AttachManager  
    public function removeAllClips() {
        //remove all clips in holding array
        for(i=0; i < attacharr.length;)

It is a class, just like one would create in any other OO type language. By using a combination of components, library resources, stage Action Script, and external Action Script (.as files), we can make some pretty powerful Flash applications. Oh, if you want to get into writing Action Script seriously, I would recommend that you download the Sepy Action Script editor, which is available here.

Important note: Although the stage is convenient, it can quickly becoming cluttered with layers/objects/animations if you only use the stage. A better way is to develop separate parts of the UI which all do specific tasks, and then create these tasks in small components (MovieClip objects are best) that are brought to the stage either manually or by Action Script (which is discussed in the Resources section of this article), which keeps the stage nice and tidy. A really good Flash developer will probably have a single layer within the stage that has one frame which has actions associated with it, which will load all the other UI parts to the stage programmatically. Of course, each component will also have its own stage; that's how Flash works. Each MovieClip has its own stage.

So I hope that gives a basic understanding of the very basic idea behind how to create Flash applications.

WPF

WPF is a truly OO language that also borrows ideas from ASP.NET. For example, there is a view and code-behind. The view part would be constructed of code known as XAML (Extensible Markup Language), which is a Microsoft proprietary language, and looks a little like XML. But it's way more powerful than simple XML. It is possible to make an entire event/animated data-driven application purely in XAML without any code-behind at all. In the case where a code-behind file is used, the code-behind file will be either C# (the correct choice) or VB.NET (no, don't do it).

So how does this all work, two files?

Let's consider a little example, a simple window with a single button control on it. That's it.

This would result in the following XAML:

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="UntitledProject1.Window1"
    x:Name="Window"
    Title="Window1"
    Width="640" Height="480">

    <Grid x:Name="LayoutRoot">
        <Button HorizontalAlignment="Left" 
          Margin="101,118,0,0" x:Name="btn1" 
          VerticalAlignment="Top" Width="188" 
          Height="67" Content="Button"/>
    </Grid>
</Window>

And we can access the Button control (btn1) from a code-behind file just fine, as shown below:

But how can this be? Well, what happens is that there is an extra code file generated behind the scenes, which contains all the objects that are defined in the XAML file, which allows the code-behind file to access the XAML defined controls. This miracle is down to partial class support.

Shall we have a look at the generated code file for this simple example, which is a filled called Window1.g.cs which will be placed in the \Obj folder as part of the compilation process.

#pragma checksum "..\..\Window1.xaml" \
  "{406ea660-64cf-4c82-b6f0-42d48172a799}" 
  "FADEDA8B7804EA63C81E418EDBA03A95"
//----------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:2.0.50727.1378
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//----------------------------------------------------------------------------

using System;
using System.Windows;
using System.Windows.Automation;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Effects;
using System.Windows.Media.Imaging;
using System.Windows.Media.Media3D;
using System.Windows.Media.TextFormatting;
using System.Windows.Navigation;
using System.Windows.Shapes;


namespace UntitledProject1 {
    
    /// <summary>
    /// Window1
    /// </summary>
    public partial class Window1 : System.Windows.Window, 
                   System.Windows.Markup.IComponentConnector {
        
        internal UntitledProject1.Window1 Window;
        internal System.Windows.Controls.Grid LayoutRoot;
        internal System.Windows.Controls.Button btn1;
        
        private bool _contentLoaded;
        
        /// <summary>
        /// InitializeComponent
        /// </summary>
        [System.Diagnostics.DebuggerNonUserCodeAttribute()]
        public void InitializeComponent() {
            if (_contentLoaded) {
                return;
            }
            _contentLoaded = true;
            System.Uri resourceLocater
                 = new System.Uri("/UntitledProject1;component/window1.xaml",
                                    System.UriKind.Relative);
            
            #line 1 "..\..\Window1.xaml"
            System.Windows.Application.LoadComponent(this, resourceLocater);
            
            #line default
            #line hidden
        }
        
        [System.Diagnostics.DebuggerNonUserCodeAttribute()]
        [System.ComponentModel.EditorBrowsableAttribute(
                             System.ComponentModel.EditorBrowsableState.Never)]
        [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute(
          "Microsoft.Design", 
          "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")]
        void System.Windows.Markup.IComponentConnector.Connect(
                                           int connectionId, object target) {
            switch (connectionId)
            {
	            case 1:
		            this.Window = ((UntitledProject1.Window1)(target));
		            return;
	            case 2:
		            this.LayoutRoot = ((System.Windows.Controls.Grid)(target));
		            return;
	            case 3:
		            this.btn1 = ((System.Windows.Controls.Button)(target));
		            return;
            }
            this._contentLoaded = true;
        }
    }
}

And our code-behind file (C#):

using System;
using System.IO;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Navigation;

namespace UntitledProject1
{
    public partial class Window1
    {
        public Window1()
        {
            this.InitializeComponent();
            
            // Insert code required on object creation below this point.
        }
    }
}

So from these three listings, you should be able to see how controls are accessible via XAML and code-behind files. But how do we use these controls? Well, they are just classes, so we can call their methods, set the properties, and listen to their events. As .NET is 100% OO, we can sub class these standard classes and override the relevant methods should we wish to. And of which would occur in a XAML file or a code-behind file. That said, control events will normally be handled in code-behind, where custom logic may be performed.

Unlike Flash, there is no stage; there is a timeline feature used for animations, but that is about it. I won't discuss that yet, as it is covered later on. This section was just meant to give you an idea of how Flash and WPF went about doing their thing.

Controls

Flash

Flash doesn't come with many controls as standard, as can be seen from the screenshots shown below.

flashcomponents.png

This doesn't seem like a very big control set when compared to the .NET 3.0 controls available (remember, we can still use System.Windows.Forms.Dll for dialogs, and can even host Windows Forms components in a WPF application), but what you need to understand with Flash is that we can apply actions (Action Script) to anything, not just controls. This is way different from the Windows programming model, where a control events are what we use to cause something to happen. For example, I could have a single frame within the main stage that has Action Script applied to it, or I could have a movie which is loaded dynamically and that could have its own actions associated within it, and it could also contain any of the controls shown above, all of which expose their own events. In fact, any object in Flash can be converted to a Graphic/MovieClip or Button, all of which will support Action Script being attached. Also remember that any AS2 code can also create external Action Script objects.

So, on the face of it, Flash's lack of a rich control hierarchy doesn't seem to be much of a problem.

But what about custom controls?

Another nice thing about Flash (well, at least AS2/ AS3 version of Flash) is that you can also sub class these standard controls, just as you would for a WPF/ WinForms control. So it's also fairly extensible.

I have Flash MX 2004 installed, and if I examine the folder C:\Program Files\Macromedia\Flash MX 2004\en\First Run\Classes\mx\controls, which is where all the standard component source code is stored, I can see a load of different classes.

flashcontrols.png

So what do these look like? Shall we have a look at one of these? Let's pick a nice simple control, say a button. Well, the AS2 code for this is shown below.

I know this is a lot of code, but I thought it might be of interest to see how a standard Flash control was created:

//****************************************************************************
//Copyright (C) 2003 Macromedia, Inc. All Rights Reserved.
//The following is Sample Code and is subject to all restrictions on
//such code as contained in the End User License Agreement accompanying
//this product.
//****************************************************************************
import mx.controls.SimpleButton;
import mx.core.UIObject;
import mx.core.UIComponent;

/**
* @tiptext click event
* @helpid 3168
*/
[Event("click")]

[TagName("Button")]
[IconFile("Button.png")]

/**
* Button class
* - extends SimpleButton
* - adds label and text with layout
* - adds ability to resize without distorting the skin
* @tiptext Button provides core button functionality. Extends SimpleButton
* @helpid 3043
*/

class mx.controls.Button extends SimpleButton
{
/**
* @private
* SymbolName for object
*/
    static var symbolName:String = "Button";
/**
* @private
* Class used in createClassObject
*/
    static var symbolOwner = mx.controls.Button;
    var    className:String = "Button";

    function Button()
    {
    }

    #include "../core/ComponentVersion.as"

/**
* number used to offset the label and/or icon when button is pressed
*/
    var  btnOffset:Number = 0;
/**
*@private
* Color used to set the theme color
*/
    var _color = "buttonColor";
/**
*@private
* Text that appears in the label if no value is specified
*/
    var __label:String  = "default value";
/**
*@private
* default label placement
*/
    var __labelPlacement:String  = "right";
/**
//*@private
* store the linkage name of the icon at initalization
*/
var initIcon;
/**
* @private
* button state skin variables
*/
    var falseUpSkin:String  = "ButtonSkin";
    var falseDownSkin:String  = "ButtonSkin";
    var falseOverSkin:String = "ButtonSkin"
    var falseDisabledSkin:String = "ButtonSkin";
    var trueUpSkin:String = "ButtonSkin";
    var trueDownSkin:String = "ButtonSkin";
    var trueOverSkin:String = "ButtonSkin";
    var trueDisabledSkin:String = "ButtonSkin";

    var falseUpIcon:String = "";
    var falseDownIcon:String = "";
    var falseOverIcon:String = "";
    var falseDisabledIcon:String = "";
    var trueUpIcon:String = "";
    var trueDownIcon:String = "";
    var trueOverIcon:String = "";
    var trueDisabledIcon:String = "";

/**
* @private
* list of clip parameters to check at init
*/
    var clipParameters:Object = { labelPlacement:1, icon:1, toggle:1, 
                                 selected:1, label:1 };
    static var mergedClipParameters:Boolean
    = UIObject.mergeClipParameters(mx.controls.Button.prototype.clipParameters, 
                                   SimpleButton.prototype.clipParameters);
    var labelPath:Object;
    var hitArea_mc:MovieClip;
    var _iconLinkageName:String;
    var centerContent : Boolean = true;
    var borderW : Number = 1;// buffer value for border

/**
* @private
* init variables. Components should implement this method and call super.init() to
* ensure this method gets called. The width, height and clip parameters will not
* be properly set until after this is called.
*/
    function init(Void):Void
    {
        super.init();
    }

/**
* @private
*
*/
    function draw()
    {
        super.draw();
        if (initIcon != undefined)
            _setIcon(initIcon);
                delete initIcon;

    }

/**
* This method calls SimpleButton's onRelease()
*/
    function onRelease(Void):Void
    {
        super.onRelease();
    }

/**
* @private
* create children objects. Components implement this method to create the
* subobjects in the component. Recommended way is to make text objects
* invisible and make them visible when the draw() method is called to
* avoid flicker on the screen.
*/
    function createChildren(Void):Void
    {
        super.createChildren();
    }

/**
* @private
* sets the skin state based on tag and linkage name
*/
    function setSkin(tag:Number,linkageName:String, initobj:Object):MovieClip
    {
        return super.setSkin(tag, linkageName, initobj);
    }

/**
* @private
* sets the old skin's visibility to false and sets the new skin's 
*visibility to true
*/
    function viewSkin(varName:String):Void
    {
        var skinStyle = getState() ? "true" : "false";
        skinStyle += enabled ? phase : "disabled";
        super.viewSkin(varName,{styleName:this,borderStyle:skinStyle});
    }

/**
* @private
* Watch for a style change.
*/
    function invalidateStyle(c:String):Void
    {
        labelPath.invalidateStyle(c);
        super.invalidateStyle(c);
    }

/**
* @private
* sets the color to each one of the states
*/
    function setColor(c:Number):Void
    {
        for (var i=0;i<8;i++)
        {
            this[idNames[i]].redraw(true);
        }
    }

/**
* @private
* this is called whenever the enabled state changes.
*/
    function setEnabled(enable:Boolean):Void
    {
        labelPath.enabled = enable;
        super.setEnabled(enable);
    }

/**
* @private
* sets same size of each of the states
*/
    function calcSize(tag:Number, ref:Object):Void
    {
        if ((__width == undefined) || (__height == undefined)) return;

        if(tag < 7 )
        {
            ref.setSize(__width,__height,true);
        }
    }

/**
* @private
* Each component should implement this method and lay out
* its children based on the .width and .height properties
*/
    function size(Void):Void
    {
        setState(getState());
        setHitArea(__width,__height);
        for (var i = 0; i < 8; i++)
        {
            var ref = idNames[i];
            if (typeof(this[ref]) == "MovieClip")
            {
                this[ref].setSize(__width, __height, true);
            }
        }
        super.size();
    }

/**
* sets the label placement of left,right,top, or bottom
* @tiptext Gets or sets the label placement relative to the icon
* @helpid 3044
*/
    [Inspectable(enumeration="left,right,top,bottom"defaultValue="right")]
    function set labelPlacement (val:String)
    {
        __labelPlacement = val;
        invalidate();
    }

/**
* returns the label placement of left,right,top, or bottom
* @tiptext Gets or sets the label placement relative to the icon
* @helpid 3045
*/
    function get labelPlacement():String
    {
        return __labelPlacement;
    }

/**
* @private
* use to get the label placement of left,right,top, or bottom
*/
    function getLabelPlacement(Void):String
    {
        return __labelPlacement;
    }

/**
* @private
* use to set the label placement to left,right,top, or bottom
*/
    function setLabelPlacement(val:String):Void
    {
        __labelPlacement = val;
        invalidate();
    }

/**
* @private
* use to get the btnOffset value
*/
    function getBtnOffset(Void):Number
    {
        if(getState())
        {
            var n = btnOffset;
        }
        else
        {
            if(phase == "down")
            {
                var n = btnOffset;
            }
            else
            {
                var n = 0;
            }
        }
        return n;
    }

/**
* @private
* Controls the layout of the icon and the label within the button.
* note that layout hinges on a variable, "centerContents", which is 
*set to true in Button.
* but false in check and radio.
*/
    function setView(offset:Number):Void
    {
        var n = offset ? btnOffset : 0;
        var val = getLabelPlacement();
        var iconW : Number = 0;
        var iconH : Number = 0;
        var labelW : Number = 0;
        var labelH : Number = 0;
        var labelX : Number = 0;
        var labelY : Number = 0;

        var lp = labelPath;
        var ic = iconName;

        // measure text size
        var textW = lp.textWidth;
        var textH = lp.textHeight;


        var viewW = __width-borderW-borderW;
        var viewH = __height-borderW-borderW;

        lp._visible = true;

        if (ic!=undefined) {
            iconW = ic._width;
            iconH = ic._height;
        }

        if (val ==  "left" || val == "right")
        {
            if (lp!=undefined)
            {
                lp._width = labelW = Math.min(viewW-iconW, textW+5);
                lp._height =  labelH = Math.min(viewH, textH+5);
            }

            if (val == "right")
            {
                labelX = iconW;
                if (centerContent) {
                    labelX += (viewW - labelW - iconW)/2;
                }
                ic._x =  labelX - iconW;

            }
            else
            {

                labelX = viewW - labelW - iconW;
                if (centerContent) {
                    labelX = labelX / 2;
                }
                ic._x  = labelX + labelW;
            }

            ic._y  = labelY = 0;
            if (centerContent) {
                ic._y  = (viewH - iconH )/2;
                labelY = (viewH - labelH )/2
            }

            if (!centerContent)
                ic._y += Math.max(0, (labelH-iconH)/2);


        }
        else
        {
            if (lp!=undefined) {
                lp._width = labelW = Math.min(viewW, textW+5);
                lp._height =  labelH = Math.min(viewH-iconH, textH+5);
            }


            labelX  = (viewW - labelW )/2;

            ic._x  = (viewW - iconW )/2;

            if (val == "top")
            {
                labelY = viewH - labelH - iconH;
                if (centerContent) {
                    labelY = labelY / 2;
                }
                ic._y = labelY + labelH;

            }
            else
            {
                labelY = iconH;
                if (centerContent) {
                    labelY += (viewH - labelH - iconH)/2;

                }
                ic._y = labelY - iconH;
            }

        }
        var buff = borderW + n;
        lp._x = labelX + buff;
        lp._y = labelY + buff;
        ic._x += buff;
        ic._y += buff;

    }

/**
* sets the associated label text
* @tiptext Gets or sets the Button label
* @helpid 3046
*/
    [Inspectable(defaultValue="Button")]
    function set label(lbl:String)
    {
        setLabel(lbl);
    }

/**
* @private
* sets the associated label text
*/
    function setLabel(label:String):Void
    {
        if (label=="")
        {
            labelPath.removeTextField();
            refresh();
            return;
        }

        if (labelPath == undefined)
        {
            var lp =  createLabel("labelPath", 200, label);
            lp._width = lp.textWidth + 5;
            lp._height = lp.textHeight +5;
            
            lp.visible = false;
        }
        else
        {
            labelPath.text = label;
            refresh();
        }
    }


/**
* @private
* gets the associated label text
*/
    function getLabel(Void):String
    {
        return labelPath.text;
    }

/**
* gets the associated label text
* @tiptext Gets or sets the Button label
* @helpid 3047
*/
    function get label():String
    {
        return labelPath.text;
    }


    function _getIcon(Void):String
    {
        return _iconLinkageName;
    }

/**
* sets the associated icon
* use setIcon() to set the icon
* @tiptext Gets or sets the linkage identifier of the Button's icon
* @helpid 3404
*/
    function get icon():String
    {
        if (initializing)
            return initIcon;
        return _iconLinkageName;
    }

/**
*@private
* sets the icon for the falseUp, falseDown and trueUp states
* use setIcon() to set the icon
*/
    function _setIcon(linkage):Void
    {
        if (initializing)
        {
            if (linkage == "" ) return;
            initIcon = linkage;
        }
        else
        {
            if ( linkage == ""){removeIcons();return;} 
            super.changeIcon(0,linkage);
            super.changeIcon(1,linkage);
            super.changeIcon(4,linkage);
            super.changeIcon(5,linkage);
            _iconLinkageName = linkage;
            refresh();
        }
    }

/**
* @sets the icon for all states of the button
* @tiptext Gets or sets the linkage identifier of the Button's icon
* @helpid 3048
*/
    [Inspectable(defaultValue="")]
    function set icon(linkage)
    {
        _setIcon(linkage);
    }

/**
* @private
* @method to set the hit area dimensions
*/
    function setHitArea(w:Number,h:Number)
    {
        if (hitArea_mc == undefined)
            createEmptyObject("hitArea_mc",100); //reserved depth for hit area
        var ha = hitArea_mc;
        ha.clear();
        ha.beginFill(0xff0000);
        ha.drawRect(0,0,w,h);
        ha.endFill();
        ha.setVisible(false);
    }

    <bindable>
    [ChangeEvent("click")]
    var _inherited_selected : Boolean;

}

This looks pretty high level, doesn't it? Looks quite Java/C# like, I would say. Curly braces/attributes, mmm.

In later versions of Flash, it is also now possible to import a control from a third party that has developed a new control using Action Script movie clips etc.

WPF

If we consider the standard controls that WPF provides (screenshot of Expression Blend) shown below:

We can see straight away that there are a lot more controls. These controls can also be subclassed, which makes WPF very powerful. However, we can still really only carry out operations based on user interaction with pre-existing control events or generated events within non UI classes. Remember, in Flash, any frame or any object could potentially be made to run Action Script. So although there are more controls within WPF, there could potentially be many crazy controls within Flash, if we consider a MovieClip with actions on it to be a User Control. Which I think I would, wouldn't you?

Resources

Flash

Resources are very, very important within Flash, and resources are placed in a library. The library can hold all sorts of things, such as sounds, graphics, MovieClips, Tweens etc. Once an object is stored in the library and has been given a name (which will have to be allowed to be stored in the library in the first place), it can be dragged to the stage and used on any of the stage layers. But that's not all that the library allows us to do. There is also another very important role the library plays, and that is the role of linkage.

Linkage is a key topic, so listen up. What linkage does is allow an object in the library to be manipulated programmatically by Action Script. This will include bringing the object onto the stage / removing the object from the stage, calling the object's methods, subscribing to the object events. It basically exposes all the library object functions to Action Script.

In fact, this is how any good Flash developer should be doing things. It is all about the linkage. By creating small components (MovieClips really) and only bringing them into life (the stage) when needed, Flash is able to leverage some sort of JIT (Just In Time) model. I would think this would be the equivalent of declaring a new object in C# like SomeObject so = new SomeObject();. Shall we see an example of this?

The following diagram illustrates this. It shows the library with a MovieClip object, which has a linkage name of "moviePhoneOFF", which allows the MovieClip to be created and manipulated via Action Script, as I am doing in this example.

WPF

Resources are also an important part of WPF, but they are fairly different from the way Flash talks about resources. We have two things to consider when discussing resources in WPF; firstly, at file level, and then at application level. Let's talk about the file level first.

For this next section to make any sense, you will probably need to be using Visual Studio 2005/2008.

You can use file properties to indicate what actions the project system should perform on the files.

BuildAction property

The BuildAction property indicates what Visual Studio does with a file when a build is executed. BuildAction can have one of several values:

  • None - The file is not included in the project output group, and is not compiled in the build process. An example is a text file that contains documentation, such as a Readme file.
  • Compile - The file is compiled into the build output. This setting is used for code files.
  • Content - The file is not compiled, but is included in the Content output group. For example, this setting is the default value for a .htm or other kind of web file.
  • Embedded Resource - This file is embedded in the main project build output as a DLL or executable. It is typically used for resource files. Specifying "Embedded Resource" puts the resource in the .mresource section of the assembly.
  • Page - Build action "Page" generates a performant BAML file.
  • Resource - Build action "Resource" generates an embedded XAML file.

The default value for BuildAction depends on the extension of the file you add to the solution. For example, if you add a Visual Basic project to the Solution Explorer, the default value for BuildAction is Compile, because the extension .vb indicates a code file that can be compiled. File names and extensions appear in Solution Explorer.

An example of this is shown below for a resource file Dictionary1.xaml within a Visual Studio WPF project:

That's how we specify a build action, but that's only half the story. We still need to create the resource files in the first place. We are able to use resource files which will allow us to add strings, images, and icons. This is the same as .NET 2.0. But what about XAML resource files that are loaded dynamically? Well, those are also called resources, but a special type of resource called a ResourceDictionary, which can contain markup that when imported can be used within the consumer of the ResourceDictionary.

So how do we create on of these ResourceDictionarys? Well, in XAML, we would do the following:

<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    >
    //makrup resources to be defined here
</ResourceDictionary>

This would allow another XAML/code-behind file to use these resources. So you can kind of think of this as the linkage step if you are from a Flash background. What we do is create a MergedDictionary which will contain the XAML resource file. Once that step is completed, the resources within the MergedDictionary may be used within code. Let's see an example of how to define a MergedDictionary:

<ResourceDictionary>
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="Dictionary1.xaml"/>
    </ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

Layout

Flash

Flash doesn't really have any layout operations such as Dock or Anchor. It doesn't have any container controls as such. Any object is simply placed right on to a canvas (the stage really). So there is not much to mention about this.

WPF

WPF on the other hand has a lot of controls which specifically deal with layout. These control are all Panels of some sort. There is:

StackPanel: The StackPanel lays out child elements by stacking them one after the other. Elements are "stacked" in the order they appear in the XAML file (document order in XML terms). Items can either be stacked vertically (the default), or horizontally.

<StackPanel>
  <TextBlock FontSize="16" Foreground="#58290A">
    Items inside a StackPanel</TextBlock>
  <Button>Item 2</Button>
  <Border BorderBrush="#feca00" BorderThickness="2">
    <TextBlock>Item 3</TextBlock>
  </Border>
</StackPanel>

<StackPanel Orientation="Horizontal">
  <TextBlock FontSize="16"
             Foreground="#58290A">
    Items inside a StackPanel</TextBlock>
  <Button>Item 2</Button>
  <Border BorderBrush="#feca00" BorderThickness="2">
    <TextBlock>Item 3</TextBlock>
  </Border>
</StackPanel>

WrapPanel: The WrapPanel lays out items from left to right. When a row of items has filled the horizontal space available to them, the panel wraps the next item around onto the next line (in a similar way to how text is laid out).

<WrapPanel>
  <TextBlock FontSize="16"
             Foreground="#58290A">
    Items inside a WrapPanel</TextBlock>
  <Button>Item 2</Button>
  <Border BorderBrush="#feca00" BorderThickness="2">
    <TextBlock>Item 3</TextBlock>
  </Border>
</WrapPanel>

DockPanel: The DockPanel allows elements to be docked to the edges of the panel's container, similar to Windows Forms docking. Items are docked in the order they appear in the XAML file (document order). The last Border element fills all the remaining space since no DockPanel.Dock attribute is specified for it.

<DockPanel>
  <TextBlock FontSize="16" DockPanel.Dock="Top"
             Foreground="#58290A">
    Items inside a DockPanel</TextBlock>
  <Button DockPanel.Dock="Left">Item 2</Button>
  <Border BorderBrush="#feca00" BorderThickness="2">
    <TextBlock>Item 3</TextBlock>
  </Border>
</DockPanel>

Canvas: The Canvas panel is similar to the way old rich-client layout worked, where you control the absolute position of things by setting a Top and Left properties for them. Additionally, instead of setting the Left to position an item, you can set the Right, or instead of setting the Top, you can set the Bottom. If you specify a Left and a Right, the Right value is ignored, the element does not change its size to make both these values correct, and similarly Top takes precedence over Bottom. Elements that are declared earlier in the XAML file can appear behind elements that are declared later (if their positions over-lap).

<Canvas>
  <TextBlock FontSize="16" 
        Canvas.Top="10" Canvas.Left="20"
        Foreground="#58290A">
    Items inside a Canvas</TextBlock>
  <Button Canvas.Bottom="25" 
         Canvas.Right="50">Item 2</Button>
  <Border BorderBrush="#feca00" BorderThickness="2"
          Canvas.Top="20" Canvas.Left="50">
    <TextBlock>Item 3</TextBlock>
  </Border>
</Canvas>

Grid: The Grid panel is an extremely flexible panel, and can be used to achieve almost everything that can be done with the other panel controls (although often not with the same ease). The Grid panel allows you to define rows and columns in the grid using XAML, and then place controls in columns in the grid using grid-specific attributes. Elements can span multiple rows or columns. The grid will automatically make all its rows and columns the same size (based on the size of the content), but you can specify a proportional size for rows and columns using a star notation, or specify an absolute width or height. The star notation can be seen in the example code below, where one of the columns is twice the width of the other column by setting the Width attribute to "2*". The example below also shows the height of one row being set to an absolute value. The differences these introduce can more readily be seen when re-sizing the form containing the grid, as the grid will expand by default to fill the space available to it.

<Grid Margin="10" ShowGridLines="True">
  <Grid.ColumnDefinitions>
    <ColumnDefinition Width="2*" />
    <ColumnDefinition />
  </Grid.ColumnDefinitions>
  <Grid.RowDefinitions>
    <RowDefinition Height="25" />
    <RowDefinition />
    <RowDefinition  Height="2*"/>
  </Grid.RowDefinitions>
  <TextBlock FontSize="16"
             Foreground="#58290A"
             Grid.Column="0" Grid.Row="0"
             Grid.ColumnSpan="2"
             >
    Items inside a Grid</TextBlock>
  <Button Grid.Column="0" Grid.Row="1">
    Item 2</Button>
  <Border BorderBrush="#feca00" BorderThickness="2"
          Grid.Column="1" Grid.Row="2">
    <TextBlock>Item 3</TextBlock>
  </Border>
</Grid>

Databinding

Flash

In Flash MX 2004 upwards, there are a whole host of new data access controls added; these are shown below:

It is by using these new controls that Flash is able to perform Databinding; it also has the usual controls that one would want to bind data to, such as:

  • DataGrid
  • ComboBox
  • CheckBox
  • TextBox
  • List

As can be seen, Flash supports DataSets, XMLConnector, and WebServiceConnector. These are probably the most typical components used. Flash is a little strange in that it needs to communicate with the page that is holding the Flash object (remember, Flash is client side, the database is server side) to grab data from the underlying database. Typically, Flash would use a DataSet to bind to a DataGrid. The DataSet would be populated by an XMLConnector which holds XML data that gets populated via Flash talking to its hosting page. This could be PHP or an ASP page, for example. Flash could also talk and bind directly to a Web Service. The three links below discuss each of these options in more detail, should you be interested.

The Flash documentation is also very good, you just have to search it. The References section at the bottom of this article contains a link to the Flash documentation.

WPF

I have to say that I think WPF has the edge on databinding. It's truly insane what you can do with databinding in WPF. You can bind a property from the following sources:

  • Bind a control property to another control's properties
  • Bind a control to a Web Service result
  • Bind a control to an XML datasource (RSS/XML document etc.)
  • Bind a control to a DataSet
  • Bind a control to the result of a Language Integrated Query (LINQ project); LINQ itself is well interesting stuff, and well worth getting into by itself; have a look and you will be amazed
  • Bind to an arbitrary .NET CLR object (such as a C# Person class, say)

It is basically quite mad. To be honest, it is probably a bit too much to go into in this comparison article. I would, however, suggest that if you want to know more about the way WPF binding works, you could read the following articles:

Styles / Templates / Skins and Themes

Flash

Flash doesn't have any concept of any of these quite WPF techniques. Though one possible solution would be to use XML files to dictate Styles, Postions, Skin Colors etc. Flash has excellent XML support, so this is the route I would go and have seen used.

WPF

Windows Presentation Foundation (WPF) styling and templating refer to a suite of features (styles, templates, triggers, and storyboards) that allow an application, document, or user interface (UI) designer to create visually compelling applications and to standardize on a particular look for their product. An author or designer can customize a look extensively on an application-by-application basis, but a strong styling and templating model is necessary to allow maintenance and sharing of a look. Windows Presentation Foundation (WPF) provides that model.

Styles

You can think of a Style as a convenient way to apply property values. Let's consider this small example which Styles a standard button:

<Style TargetType="Button">
  <!--Set to true to not get any properties from the themes.-->
  <Setter Property="OverridesDefaultStyle" Value="True"/>
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="Button">
        <Grid>
          <Ellipse Fill="{TemplateBinding Background}"/>
          <ContentPresenter HorizontalAlignment="Center"
                            VerticalAlignment="Center"/>
        </Grid>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

Which when applied to a Button results in:

Templates

For most controls, there is appearance and behavior. Consider a button: appearance is the raised area that you can press, and the behavior is the Click event that gets raised in response to a click.

Sometimes, there may be a control that provides the behavior that you need, but not the appearance that you need. So far, we have seen that you can use style setters to set property values to affect the look of a control. However, to change the structure of a control or to set property values on the components that comprise a control, you need to use a ControlTemplate.

In WPF, the ControlTemplate of a control defines the appearance of the control. You can change the structure and appearance of a control by defining a new ControlTemplate for the control. In many cases, this gives you enough flexibility so that you don't have to write your own custom controls. If you do not define your own ControlTemplate for your control, you get the default template that matches the system theme, which is what gives the Button control its default look.

One thing to keep in mind is that once you create a ControlTemplate for you control, you are replacing the entire ControlTemplate. For example, you may define your Button's ControlTemplate the following way:

<Style TargetType="ListBox">
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="ListBox">
        <Border CornerRadius="5" 
           Background="{TemplateBinding ListBox.Background}">
          <ScrollViewer HorizontalScrollBarVisibility="Auto">
            <StackPanel Orientation="Horizontal"
                       VerticalAlignment="Center"
                       HorizontalAlignment="Center"
                       IsItemsHost="True"/>
          </ScrollViewer>
        </Border>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

This example defines a ControlTemplate for a ListBox.

Skins and Themes

I'm going to be a bit cheeky here and simply direct you to a full article on this, as the explanation is much better there. It's by Josh Smith (surprise, surprise), and the link is located right here.

2D graphics / 3D graphics

Flash

Flash is basically a vector based animation environment, but there are options available for 3D. Most notably, Swift3D which allows a user to create a 3D model and export straight into Flash.

WPF

WPF has good support for both vector and 3D. It has native support for 3D built into the framework. For example, let's consider a 3D button style. The code is as shown below:

<!-- 3D Media Buttons -->
  <Style x:Key="btn3DStyle"  TargetType="{x:Type Button}">
    <Style.Resources>
      <Storyboard x:Key="Spin">
        <DoubleAnimation
          Storyboard.TargetName="CubeRotation"
          Storyboard.TargetProperty="Angle" 
          BeginTime="0:0:0"
          Duration="0:0:1" From="0" 
          To="360" DecelerationRatio="0.5" 
          AccelerationRatio="0.5" />
        <DoubleAnimation
          Storyboard.TargetName="CubeRotation"
          Storyboard.TargetProperty="Angle" 
          BeginTime="0:0:1"
          Duration="0:0:1" From="360" 
          To="0" DecelerationRatio="0.5" 
          AccelerationRatio="0.5" />
        <DoubleAnimation
          Storyboard.TargetName="CubeScale"
          Storyboard.TargetProperty="ScaleX"
          BeginTime="0:0:0"
          Duration="0:0:1" 
          From="0.5" To="0.75" />
        <DoubleAnimation
          Storyboard.TargetName="CubeScale"
          Storyboard.TargetProperty="ScaleX"
          BeginTime="0:0:1"
          Duration="0:0:1" From="0.75" 
          To="1.0" />
        <DoubleAnimation
          Storyboard.TargetName="CubeScale"
          Storyboard.TargetProperty="ScaleY"
          BeginTime="0:0:0"
          Duration="0:0:1" From="0.5" 
          To="0.75" />
        <DoubleAnimation
          Storyboard.TargetName="CubeScale"
          Storyboard.TargetProperty="ScaleY"
          BeginTime="0:0:1"
          Duration="0:0:1" From="0.75" 
          To="1.0" />
        <DoubleAnimation
          Storyboard.TargetName="CubeScale"
          Storyboard.TargetProperty="ScaleZ"
          BeginTime="0:0:0"
          Duration="0:0:1" From="0.5" 
          To="0.75" />
        <DoubleAnimation
          Storyboard.TargetName="CubeScale"
          Storyboard.TargetProperty="ScaleZ"
          BeginTime="0:0:1"
          Duration="0:0:1" From="0.75" 
          To="1.0" />
      </Storyboard>
    </Style.Resources>

    <Setter Property="Width" Value="100"/>
    <Setter Property="Height" Value="100"/>    
    
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate>

          <ControlTemplate.Triggers>
            <Trigger Property="Button.IsMouseOver" Value="true">
              <Trigger.EnterActions>
                <BeginStoryboard Storyboard="{StaticResource Spin}"/>
              </Trigger.EnterActions>
            </Trigger>
          </ControlTemplate.Triggers>

          <Viewport3D>
            <Viewport3D.Camera>
              <PerspectiveCamera Position="4,4,4" 
                  LookDirection="-1,-1,-1" />
            </Viewport3D.Camera>
            <Viewport3D.Children>
              <ModelVisual3D>
                <ModelVisual3D.Content>
                  <DirectionalLight Direction="-0.3,-0.4,-0.5" />
                </ModelVisual3D.Content>
              </ModelVisual3D>
              <ModelVisual3D x:Name="Cube">
                <ModelVisual3D.Transform>

                  <Transform3DGroup>
                    <RotateTransform3D>
                      <RotateTransform3D.Rotation>
                        <AxisAngleRotation3D x:Name="CubeRotation" 
                           Axis="1,2,3" Angle="0" />
                      </RotateTransform3D.Rotation>
                    </RotateTransform3D>
                    <ScaleTransform3D x:Name="CubeScale" 
                         ScaleX="1" ScaleY="1" 
                         ScaleZ="1" CenterX="0" 
                         CenterY="0" CenterZ="0" />
                  </Transform3DGroup>

                </ModelVisual3D.Transform>
                <ModelVisual3D.Content>
                  <GeometryModel3D x:Name="OB_Cube">
                    <GeometryModel3D.Material>
                      <DiffuseMaterial>
                        <DiffuseMaterial.Brush>
                          <VisualBrush ViewportUnits="Absolute" 
                                   Transform="1,0,0,-1,0,1">
                            <VisualBrush.Visual>
                              <Border 
                                  Background="{Binding Path=
                                    Background, RelativeSource=
                                    '{RelativeSource TemplatedParent}'}">
                                <Label 
                                  Content="{Binding Path=Content, 
                                    RelativeSource='{RelativeSource 
                                    TemplatedParent}'}" />
                              </Border>
                            </VisualBrush.Visual>
                          </VisualBrush>
                        </DiffuseMaterial.Brush>
                      </DiffuseMaterial>
                    </GeometryModel3D.Material>
                    <GeometryModel3D.Geometry>
                      <MeshGeometry3D x:Name="ME_Cube"
                        Positions="1,1,-1 1,-1,-1 -1,-1,-1 -1,1,
                           -1 1,1,1 -1,1,1 -1,-1,1 1,-1,1 1,1,-1 1,1,
                           1 1,-1,1 1,-1,-1 1,-1,-1 1,-1,1 -1,-1,
                           1 -1,-1,-1 -1,-1,-1 -1,-1,1 -1,1,1 -1,1,
                           -1 1,1,1 1,1,-1 -1,1,-1 -1,1,1"
                        TriangleIndices="0 1 2 0 2 3 4 5 6 4 6 
                             7 8 9 10 8 10 11 12 13 14 12 14 15 16 17 
                             18 16 18 19 20 21 22 20 22 23"
                        TextureCoordinates="0,1 0,0 1,0 1,1 1,1 -0,
                             1 0,-0 1,0 1,1 -0,1 0,-0 1,0 1,0 1,1 -0,1 0,
                             -0 -0,0 1,-0 1,1 0,1 1,-0 1,1 0,1 -0,0"/>
                    </GeometryModel3D.Geometry>
                  </GeometryModel3D>
                </ModelVisual3D.Content>
              </ModelVisual3D>
            </Viewport3D.Children>
          </Viewport3D>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>

which results in a normal button which shows video on its surface as a 3D cube, as shown below:

Animations

Flash

Flash allow users to create almost limitless animations. We can use motion paths, create tweens, which use key frames to allow us to specify certain frames that dictate how an object will look, and the parts in between are interpolated. At key frames, we can specify the size, color, position, opacity, rotation, anything really.

In my mind, when it comes to animations, Flash wins hands down; no contest, not even close from anything else ever. It's the daddy, brother, sister, and mother, second cousin etc., all in one. It rocks. And why is this? Well, it's to do with two reasons really.

Reason 1: The stage in Flash

Recall that the stage has layers down and time across. Not that great, you might say. But what it does is, allow the user to have different objects on the same layer at different times within the stage. For example, let's say we have the following:

A single layer with a square at Frame1, and a circle at Frame2:

Does this seem cool yet? No, you say. Well, it is. This is why. We can have anything we like at a given frame within a layer. The object that exists at a frame can then be animated. And if we can bring in new items along the stage at any frame we want, and we consider this across all layers, and then consider that each MovieClip can have such an arrangement, and that any number of MovieClips can be brought into the main stage either manually or programmatically, we all of a sudden have a very powerful animation environment. And if that isn't enough, we can also do all this though code using a third party Action Script that some mad guy wrote to allow a user to control tweens (a fundamental Flash animation object) through Action Script.

All that needs to be done is that the tweening library is referenced in the main stage Action Script, as shown below:

#include "lmc_tween.as"

Each MovieClip object can then be controlled via Action Script, such as using the alphaTo method.

Flash Player 6
Usage
my_mc.alphaTo(alpha, seconds, animtype, delay, callback, extra1, extra2)
Parameters

alpha is end value of the MovieClip _alpha property; all other parameters work as in the tween() method.

Returns

None.

An example might be (this is used within BlockManager.as of FlashDemo1.zip):

tmpLoc.alphaTo(100,.1,'linear',i/20,{func:'playClip',scope:this,args:[tmpLoc]})

The third party tweening library is available as a .MXP, which is a Macromedia Extension file, and is officially available at the author's site, which is http://laco.wz.cz/tween/?page=download - this site also has the API documentation.

WPF

WPF does indeed support animations. But to my mind, it's not a touch on Flash. The reason being that there isn't a concept of a stage; there is a timeline, but that is only used in DoubleAnimationUsingKeyFrames. Whereas, Flash uses time everywhere, and you can even swap out what is shown on a layer at time(x).

To further understand what I mean, and why Flash is better, we need to understand how WPF apps are created. They are created using either XAML, which is a tree type structure, or by using code-behind procedural code. So why is this not as flexible as Flash? Well, in both cases, the content of the page is not that dynamic. In the case of XAML, the tree is fairly static; what is there at design time is what we get at runtime. OK, we could add new controls which we could animate in the code-behind, which in fact is a partial solution, but it just seems clumsy to me. We could also have different pages that are loaded one after another, or we could simply destroy the current XAML tree and load in a new XAML document tree from disk, and that would offer some sort of flexibility.

I just think Flash has it better. Layers with whatever you want at any frame, and time used in all stages everywhere, and the ability to bring items to and from the stage at will. I mean, have a look at FlashDemo3.zip and see how easy you think that would be to do in WPF, let alone Silverlight, which is a cut-down version of WPF anyway.

Anyway, to use animations in WPF, we can either use XAML or procedural code. Let's have a look, shall we?

There are two options available in WPF: you can use DoubleAnimation, or use DoubleAnimationUsingKeyFrames.

DoubleAnimation
XAML
<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  WindowTitle="Fading Rectangle Example">
  <StackPanel Margin="10">

    <Rectangle
      Name="MyRectangle"
      Width="100" 
      Height="100"
      Fill="Blue">
      <Rectangle.Triggers>

        <!-- Animates the rectangle's opacity. -->
        <EventTrigger RoutedEvent="Rectangle.Loaded">
          <BeginStoryboard>
            <Storyboard>
              <DoubleAnimation
                Storyboard.TargetName="MyRectangle" 
                Storyboard.TargetProperty="Opacity"
                From="1.0" To="0.0" Duration="0:0:5" 
                AutoReverse="True" RepeatBehavior="Forever" />
            </Storyboard>
          </BeginStoryboard>
        </EventTrigger>
      </Rectangle.Triggers>
    </Rectangle>
  </StackPanel>
</Page>
C#
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Shapes;
using System.Windows.Media;
using System.Windows.Media.Animation;

namespace SDKSample
{
    public class RectangleOpacityFadeExample : Page
    {
        private Storyboard myStoryboard;

        public RectangleOpacityFadeExample()
        {
            NameScope.SetNameScope(this, new NameScope());

            this.WindowTitle = "Fading Rectangle Example";
            StackPanel myPanel = new StackPanel();
            myPanel.Margin = new Thickness(10);

            Rectangle myRectangle = new Rectangle();
            myRectangle.Name = "myRectangle";
            this.RegisterName(myRectangle.Name, myRectangle);
            myRectangle.Width = 100;
            myRectangle.Height = 100;
            myRectangle.Fill = Brushes.Blue;

            DoubleAnimation myDoubleAnimation = new DoubleAnimation();
            myDoubleAnimation.From = 1.0;
            myDoubleAnimation.To = 0.0;
            myDoubleAnimation.Duration = new Duration(TimeSpan.FromSeconds(5));
            myDoubleAnimation.AutoReverse = true;
            myDoubleAnimation.RepeatBehavior = RepeatBehavior.Forever;

            myStoryboard = new Storyboard();
            myStoryboard.Children.Add(myDoubleAnimation);
            Storyboard.SetTargetName(myDoubleAnimation, myRectangle.Name);
            Storyboard.SetTargetProperty(myDoubleAnimation, 
               new PropertyPath(Rectangle.OpacityProperty));

            // Use the Loaded event to start the Storyboard.
            myRectangle.Loaded += new RoutedEventHandler(myRectangleLoaded);  

            myPanel.Children.Add(myRectangle);
            this.Content = myPanel;
        }

        public void myRectangleLoaded(object sender, RoutedEventArgs e)
        {
            myStoryboard.Begin(this);
        }
    }
}
DoubleAnimationUsingKeyFrames

Are like normal animations, but this time, we get key frames, which is where we can say what an object will look like at time(x).

<!-- Using Paced Values. Rectangle moves between key frames at 
     uniform rate except for first key frame
     because using a Paced value on the first KeyFrame in a 
     collection of frames gives a time of zero. -->
<Rectangle Height="50" 
       Width="50" Fill="Orange">
  <Rectangle.RenderTransform>
    <TranslateTransform x:Name="TranslateTransform04" 
        X="10" Y="270" />
  </Rectangle.RenderTransform>
  <Rectangle.Triggers>
    <EventTrigger RoutedEvent="Rectangle.Loaded">
      <BeginStoryboard>
        <Storyboard>
          <DoubleAnimationUsingKeyFrames 
            Storyboard.TargetName="TranslateTransform04" 
            Storyboard.TargetProperty="X"
            Duration="0:0:10"
            RepeatBehavior="Forever">

            <!-- KeyTime properties are expressed with values of Paced. 
                 Paced values are used when a constant rate is desired. 
                 The time allocated to a key frame with a KeyTime of 
        "Paced" 
                 is determined by the time allocated to the other key 
                 frames of the animation. This time is calculated to 
                 attempt to give a "paced" or "constant velocity" 
                 for the animation. -->
            <LinearDoubleKeyFrame Value="100" KeyTime="Paced" />
            <LinearDoubleKeyFrame Value="200" KeyTime="Paced" />
            <LinearDoubleKeyFrame Value="500" KeyTime="Paced" />
            <LinearDoubleKeyFrame Value="600" KeyTime="Paced" />
          </DoubleAnimationUsingKeyFrames>
        </Storyboard>
      </BeginStoryboard>
    </EventTrigger>
  </Rectangle.Triggers>
</Rectangle>

Custom controls

Flash

As we have already seen, Flash supports the creation of custom controls, by inheriting from standard controls. Creation of totally new controls is not really the Flash way; one would probably make a MovieClip to do a task. This would be the control. There isn't really much more to be said here.

WPF

As WPF is using .NET 3.0 Framework, we can of course inherit from UserControl or Control to create new controls. We can also simply inherit and override existing control methods. It is all standard OO stuff. But it is all possible. An interesting control is shown here where Christian Graus and Nishant Sivakumar inherit from the AnimationTimeline class and get it to animate a GridLength.

Structuring applications

Like I say I am no expect in either of these fields, but this is how I would go about developing stuff for each of these worlds

Flash

Create small parts that perform a specific task. Make these MovieClip objects, which have the correct library linkage and are exported for Action Scripting. Create external Action Script objects, try to use an OO design method. Think classes. Use the Sepi editor, it's good. Keep the stage tidy.

WPF

Think about what you are trying to do, use the most appropriate method to do the job. If it's XAML oriented, use XAML; otherwise use code. Do use ValueConvertors, Resource files/ MergedDictionaries, Templates, Styles, and don't subclass everything just because that is what you did before in .NET 2.0.

Sub classing really isn't needed that often in WPF; you can usually do what you want with a Style or a Template. Oh, and don't go overboard on the animations just because you can. Again, keep classes well defined and specific to the job they should perform.

APIs

Flash

Flash does have a fairly extensive set of classes. Let's consider the following diagram:

We can see that Flash provides serveral packages, we even have a Client/Server package to deal with XML, and we can also use sockets. Quite cool really for a little plug-in.

WPF

Well, where do you start? WPF relies on the .NET 3.0 Framework, of which WPF is one part. The .NET 3.0 Framework is vast, and has literally 1000s of classes, interfaces, structures etc.

Shown below is a screenshot of the main WPF related packages of the .NET 3.0 Framework. Using these classes, a programmer is able to do all of the topics mentioned above, and of course, as they are simply classes, we can inherit from them and bend them to do our bidding. This is also what Macromedia (well, Adobe I suppose) has done with AS3 (you could inherit from other classes in AS2 as well actually). AS3 is a total rewrite, and is basically very Java like syntax now, and it also allows full OO techniques to be used - not just inheritance, but method overriding, polymorphism etc. But this article (just because I have Flash MX 2004) was all about AS2.

Anyway, here is a screenshot of the main WPF .NET Framework packages. But expect this to grow with future versions. Remember, this is the first release of WPF. And also bear in mind that most of these individual packages will have between 10-100 different classes. It's a big framework.

What if we compare the .NET 3.0 Media package with its Flash equivalent?

There are loads more .NET 3.0 media classes than there are in Flash. But I have seen some pretty cool Flash apps. And don't forget, Silverlight (WPF/e) is really the Flash equivalent, and will not have the full power of WPF to play with. It will run in a safe sandbox, and will have a subset of the full WPF APIs available. So this full list of classes may not be available to Silverlight.

My personal opinion on this is that, sometimes less is more. Flash may not have it all, but what it does have, it does really, really well. However, one could also argue that .NET 3.0 is really just an extension to .NET 2.0, so we can use all the good .NET 2.0 stuff as well, which indeed we can. Oh, there is also LINQ and Generics, which we can use in WPF but not in Flash. Mmm, the debate goes on.

Demo projects

Flash demos

I have included three zip files at the top of this article, all of which show different types of Flash applications. FlashDemo1.zip and FlashDemo3.zip are mainly animations with some AS2 external files, FlashDemo2.zip is more application like, and probably more in line with the sort of development one would do in a WPF application. I have not included a WPF application here as there are loads of finished WPF projects available here at www.codeproject.com, or just search in www.google.com.

FlashDemo1.zip

Simply extract the zip file and open Video.exe as I've made a nice executable for you.

The zip also contains all the files to view and run the Flash application if you want to. This one is all about how to use external AS2 files and to use tweens (animations in WPF), and is a short movie to a sound track.

FlashDemo2.zip

Simply extract the zip file and open MMDA_Assign2.exe as I have made a nice executable for you. The zip also contains all the files to view and run the Flash application if you want to.

It is a mobile phone demo video, and offers a phone book, simulating a call, popup help, and web cam usage.

This image may also be useful for you in order to see all the features demonstrated.

FlashDemo3.zip

This is an animated Flash site that I did for a HardCore techno record label I used to own and run, Surgeon 16 Recordings. My name was Dr Machette; in case anyone is interested, there is a discogs link to my label here and me by myself; this one on a German label called SpecialForces.

Simply extract the zip file, and open S16_Launcher.html in your favourite browser. The zip also contains all the files to view and run the Flash application if you want to.

I have not included the music files with this one; otherwise, it would be a very large download.

This one actually navigated to five areas; you can navigate to each of these areas using the Navigate buttons shown at the bottom. This was my first place project ever, and uses a lot of animation, so the stage is very busy. This is a good example of something that could probably be done tidier and could be refactored to use the library and linkage we talked about earlier.

WPF demos

I have not created any new WPF demos for this article. But I have written a few in the past, you can find those under the Windows Presentation section of my articles.

That's it

Although there is not that much code in this article, I had fun doing this one, and hope that it will be useful to someone out there.

So what do you think?

I would just like to ask, if you liked the article, please vote for it, and leave some comments, as it lets me know if the article was at the right level or not, and whether it contained what people need to know.

Conclusion

I hope that this article has covered the main areas of both Flash and WPF. For me, I like both for different reasons, but if I was going to do a pure animation app, I would use Flash, and WPF for a more data driven application. I am going to try Silverlight next, so will let you know how that goes. I will start with a simple animation app, and build from there. Anyway, if you got this far, well done.

Reference material

History

  • v1.0 - 13/09/07: Inital issue.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here